Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions rs/sol-gen/src/hbs/contract.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract {{contract_name}}Abi is I{{contract_name}} {

interface I{{contract_name}}Callbacks {
{{#each functions}}
function replyOn_{{name}}(bytes32 messageId{{#if reply_type}}, {{reply_type}} reply{{/if}}) external{{#if returns_value}} payable{{/if}};
function replyOn_{{name}}(bytes32 messageId{{#if reply_type}}, {{reply_type}}{{#if reply_mem_location}} {{reply_mem_location}}{{/if}} reply{{/if}}) external{{#if returns_value}} payable{{/if}};

{{/each}}
function onErrorReply(bytes32 messageId, bytes calldata payload, bytes4 replyCode) external payable;
Expand All @@ -52,7 +52,7 @@ contract {{contract_name}}Caller is I{{contract_name}}Callbacks {
}

{{#each functions}}
function replyOn_{{name}}(bytes32 messageId{{#if reply_type}}, {{reply_type}} reply{{/if}}) external{{#if returns_value}} payable{{/if}} onlyVaraEthProgram {
function replyOn_{{name}}(bytes32 messageId{{#if reply_type}}, {{reply_type}}{{#if reply_mem_location}} {{reply_mem_location}}{{/if}} reply{{/if}}) external{{#if returns_value}} payable{{/if}} onlyVaraEthProgram {
// TODO: implement this
}

Expand Down
28 changes: 22 additions & 6 deletions rs/sol-gen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use anyhow::Result;
use anyhow::{Result, anyhow};
use convert_case::{Case, Casing};
use handlebars::Handlebars;
use sails_idl_parser_v2::{
ast::{IdlDoc, PrimitiveType, TypeDecl},
ast::{IdlDoc, PrimitiveType, Type, TypeDecl},
parse_idl,
};
use serde::Serialize;
Expand Down Expand Up @@ -54,6 +54,22 @@ pub struct GenerateContractResult {
pub name: String,
}

fn resolve_type_decl(decl: &TypeDecl, types: &[Type]) -> Result<String> {
match decl {
TypeDecl::Named { name, .. } => types
.iter()
.find(|ty| ty.name == *name)
.and_then(|ty| ty.annotations.iter().find(|(k, _)| k == "sol-type"))
.and_then(|(_, v)| v.clone())
.ok_or_else(|| anyhow!("type is not supported")),
TypeDecl::Array { item, len } => {
Ok(format!("{}[{}]", resolve_type_decl(item, types)?, len))
}
TypeDecl::Slice { item } => Ok(format!("{}[]", resolve_type_decl(item, types)?)),
_ => decl.get_ty(),
}
}

pub fn generate_solidity_contract(idl_content: &str, name: &str) -> Result<GenerateContractResult> {
let doc = parse_idl(idl_content)?;

Expand Down Expand Up @@ -87,7 +103,7 @@ fn functions_from_idl(doc: &IdlDoc) -> Result<Vec<FunctionData>> {
let mut args = Vec::new();
for p in &func.params {
let arg = ArgData {
ty: p.type_decl.get_ty()?,
ty: resolve_type_decl(&p.type_decl, &program.types)?,
name: p.name.to_case(Case::Camel),
mem_location: p.type_decl.get_mem_location(),
};
Expand All @@ -109,14 +125,14 @@ fn functions_from_idl(doc: &IdlDoc) -> Result<Vec<FunctionData>> {
let mut args = Vec::new();
for p in &f.params {
let arg = ArgData {
ty: p.type_decl.get_ty()?,
ty: resolve_type_decl(&p.type_decl, &svc.types)?,
name: p.name.to_case(Case::Camel),
mem_location: p.type_decl.get_mem_location(),
};
args.push(arg);
}
let reply_type = if f.output != TypeDecl::Primitive(PrimitiveType::Void) {
Some(f.output.get_ty()?)
Some(resolve_type_decl(&f.output, &svc.types)?)
} else {
None
};
Expand Down Expand Up @@ -144,7 +160,7 @@ fn events_from_idl(doc: &IdlDoc) -> Result<Vec<EventData>> {
let mut args = Vec::new();
for f in &e.def.fields {
let arg = EventArgData {
ty: f.type_decl.get_ty()?,
ty: resolve_type_decl(&f.type_decl, &svc.types)?,
indexed: f.annotations.iter().any(|(k, _)| k == "indexed"),
name: f.name.as_ref().map(|name| name.to_case(Case::Camel)),
};
Expand Down
2 changes: 0 additions & 2 deletions rs/sol-gen/src/typedecl_to_sol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ impl TypeDeclToSol for TypeDecl {
fn get_ty(&self) -> Result<String> {
match self {
TypeDecl::Primitive(ty) => ty.get_ty(),
TypeDecl::Array { item, len } => Ok(format!("{}[{}]", item.get_ty()?, len)),
TypeDecl::Slice { item } => Ok(format!("{}[]", item.get_ty()?)),
_ => Err(anyhow!("type is not supported")),
}
}
Expand Down
38 changes: 37 additions & 1 deletion rs/sol-gen/tests/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ service MyService {
"#;

#[test]

fn test_generate_payable_contract() {
let contract = generate_solidity_contract(PAYABLE_IDL, "PayableContract");

Expand All @@ -140,3 +139,40 @@ fn test_generate_payable_contract() {
);
assert_snapshot!(String::from_utf8(contract.unwrap().data).unwrap());
}

const IDL_W_ADDRESS: &str = r#"
program TokenProgram {
constructors {
New();
}
services {
TokenSvc: TokenSvc
}
}

service TokenSvc {
functions {
Transfer(from: Address, to: Address, amount: u128) -> bool;
BalanceOf(owner: Address) -> u128;
Batch(recipients: [Address]) -> [Address];
Fixed(addrs: [Address; 3]) -> [Address; 3];
}

types {
@sol-type: address
struct Address(H160);
}
}
"#;

#[test]
fn test_generate_contract_w_address_type() {
let contract = generate_solidity_contract(IDL_W_ADDRESS, "TokenContract");

assert!(
contract.is_ok(),
"Failed to generate contract: {:?}",
contract.err()
);
assert_snapshot!(String::from_utf8(contract.unwrap().data).unwrap());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
source: rs/sol-gen/tests/generator.rs
expression: "String::from_utf8(contract.unwrap().data).unwrap()"
---
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

interface ITokenContract {
function new(bool _callReply) external returns (bytes32 messageId);

function tokenSvcBalanceOf(bool _callReply, address owner) external returns (bytes32 messageId);

function tokenSvcBatch(bool _callReply, address[] calldata recipients) external returns (bytes32 messageId);

function tokenSvcFixed(bool _callReply, address[3] calldata addrs) external returns (bytes32 messageId);

function tokenSvcTransfer(bool _callReply, address from, address to, uint128 amount) external returns (bytes32 messageId);
}

contract TokenContractAbi is ITokenContract {
function new(bool _callReply) external returns (bytes32 messageId) {}

function tokenSvcBalanceOf(bool _callReply, address owner) external returns (bytes32 messageId) {}

function tokenSvcBatch(bool _callReply, address[] calldata recipients) external returns (bytes32 messageId) {}

function tokenSvcFixed(bool _callReply, address[3] calldata addrs) external returns (bytes32 messageId) {}

function tokenSvcTransfer(bool _callReply, address from, address to, uint128 amount) external returns (bytes32 messageId) {}
}

interface ITokenContractCallbacks {
function replyOn_new(bytes32 messageId) external;

function replyOn_tokenSvcBalanceOf(bytes32 messageId, uint128 reply) external;

function replyOn_tokenSvcBatch(bytes32 messageId, address[] calldata reply) external;

function replyOn_tokenSvcFixed(bytes32 messageId, address[3] calldata reply) external;

function replyOn_tokenSvcTransfer(bytes32 messageId, bool reply) external;

function onErrorReply(bytes32 messageId, bytes calldata payload, bytes4 replyCode) external payable;
}

contract TokenContractCaller is ITokenContractCallbacks {
ITokenContract public immutable VARA_ETH_PROGRAM;

error UnauthorizedCaller();

constructor(ITokenContract _varaEthProgram) {
VARA_ETH_PROGRAM = _varaEthProgram;
}

modifier onlyVaraEthProgram() {
_onlyVaraEthProgram();
_;
}

function _onlyVaraEthProgram() internal view {
if (msg.sender != address(VARA_ETH_PROGRAM)) {
revert UnauthorizedCaller();
}
}

function replyOn_new(bytes32 messageId) external onlyVaraEthProgram {
// TODO: implement this
}

function replyOn_tokenSvcBalanceOf(bytes32 messageId, uint128 reply) external onlyVaraEthProgram {
// TODO: implement this
}

function replyOn_tokenSvcBatch(bytes32 messageId, address[] calldata reply) external onlyVaraEthProgram {
// TODO: implement this
}

function replyOn_tokenSvcFixed(bytes32 messageId, address[3] calldata reply) external onlyVaraEthProgram {
// TODO: implement this
}

function replyOn_tokenSvcTransfer(bytes32 messageId, bool reply) external onlyVaraEthProgram {
// TODO: implement this
}

function onErrorReply(bytes32 messageId, bytes calldata payload, bytes4 replyCode) external payable onlyVaraEthProgram {
// TODO: implement this
}
}
Loading