--- a/pallets/balances-adapter/src/erc.rs +++ b/pallets/balances-adapter/src/erc.rs @@ -1,7 +1,10 @@ use crate::Config; use evm_coder::{abi::AbiType, AbiCoder, ToLog, generate_stubgen, solidity_interface, types::*}; -use frame_support::traits::Currency; -use pallet_common::erc::{CommonEvmHandler, CrossAccountId, PrecompileHandle, PrecompileResult}; +use frame_support::traits::{Currency, ExistenceRequirement}; +use pallet_common::{ + erc::{CommonEvmHandler, CrossAccountId, PrecompileHandle, PrecompileResult}, + eth::CrossAddress, +}; use pallet_evm_coder_substrate::{ call, dispatch_to_evm, execution::{PreDispatch, Result}, @@ -82,16 +85,22 @@ // #[weight(>::transfer())] fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result { - // let caller = T::CrossAccountId::from_eth(caller); - // let to = T::CrossAccountId::from_eth(to); - // let amount = amount.try_into().map_err(|_| "amount overflow")?; + let caller = T::CrossAccountId::from_eth(caller); + let to = T::CrossAccountId::from_eth(to); + let amount = amount.try_into().map_err(|_| "amount overflow")?; // let budget = self // .recorder // .weight_calls_budget(>::find_parent()); // >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; - // Ok(true) - todo!() + ::Currency::transfer( + caller.as_sub(), + to.as_sub(), + amount, + ExistenceRequirement::KeepAlive, + ) + .map_err(dispatch_to_evm::); + Ok(true) } // #[weight(>::transfer_from())] @@ -102,24 +111,93 @@ to: Address, amount: U256, ) -> Result { - // let caller = T::CrossAccountId::from_eth(caller); - // let from = T::CrossAccountId::from_eth(from); - // let to = T::CrossAccountId::from_eth(to); - // let amount = amount.try_into().map_err(|_| "amount overflow")?; + let caller = T::CrossAccountId::from_eth(caller); + let from = T::CrossAccountId::from_eth(from); + let to = T::CrossAccountId::from_eth(to); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + + if (from != to) { + return Err("no permission".into()); + } + // let budget = self + // .recorder + // .weight_calls_budget(>::find_parent()); + + // >::transfer_from(self, &caller, &from, &to, amount, &budget) + // .map_err(dispatch_to_evm::)?; + ::Currency::transfer( + caller.as_sub(), + to.as_sub(), + amount, + ExistenceRequirement::KeepAlive, + ) + .map_err(dispatch_to_evm::); + Ok(true) + } +} + +#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))] +impl NativeFungibleHandle +where + T::AccountId: From<[u8; 32]>, +{ + // #[weight(>::transfer())] + fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = to.into_sub_cross_account::()?; + let amount = amount.try_into().map_err(|_| "amount overflow")?; + // let budget = self + // .recorder + // .weight_calls_budget(>::find_parent()); + + // >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; + ::Currency::transfer( + caller.as_sub(), + to.as_sub(), + amount, + ExistenceRequirement::KeepAlive, + ) + .map_err(dispatch_to_evm::); + Ok(true) + } + + // #[weight(>::transfer_from())] + fn transfer_from_cross( + &mut self, + caller: Caller, + from: CrossAddress, + to: CrossAddress, + amount: U256, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let from = from.into_sub_cross_account::()?; + let to = to.into_sub_cross_account::()?; + let amount = amount.try_into().map_err(|_| "amount overflow")?; + + if (from != to) { + return Err("no permission".into()); + } + // let budget = self // .recorder // .weight_calls_budget(>::find_parent()); // >::transfer_from(self, &caller, &from, &to, amount, &budget) // .map_err(dispatch_to_evm::)?; - // Ok(true) - todo!() + ::Currency::transfer( + caller.as_sub(), + to.as_sub(), + amount, + ExistenceRequirement::KeepAlive, + ) + .map_err(dispatch_to_evm::); + Ok(true) } } #[solidity_interface( name = UniqueNativeFungible, - is(ERC20), + is(ERC20, ERC20UniqueExtensions), enum(derive(PreDispatch)) )] impl NativeFungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {} --- a/pallets/balances-adapter/src/lib.rs +++ b/pallets/balances-adapter/src/lib.rs @@ -17,7 +17,7 @@ Self::AccountId, Balance = Self::CurrencyBalance, >; - type CurrencyBalance: Into; + type CurrencyBalance: Into + TryFrom; type Decimals: Get; type Name: Get; --- a/pallets/balances-adapter/src/stubs/UniqueNativeFungible.sol +++ b/pallets/balances-adapter/src/stubs/UniqueNativeFungible.sol @@ -17,6 +17,40 @@ } } +/// @dev the ERC-165 identifier for this interface is 0xff15c6f4 +contract ERC20UniqueExtensions is Dummy, ERC165 { + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 amount) public returns (bool) { + require(false, stub_error); + to; + amount; + dummy = 0; + return false; + } + + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 amount + ) public returns (bool) { + require(false, stub_error); + from; + to; + amount; + dummy = 0; + return false; + } +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + /// @dev inlined interface contract ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); @@ -25,26 +59,31 @@ /// @dev the ERC-165 identifier for this interface is 0x942e8b22 contract ERC20 is Dummy, ERC165, ERC20Events { - /// @dev EVM selector for this function is: 0x06fdde03, - /// or in textual repr: name() - function name() public view returns (string memory) { + /// @dev EVM selector for this function is: 0xdd62ed3e, + /// or in textual repr: allowance(address,address) + function allowance(address owner, address spender) public view returns (uint256) { require(false, stub_error); + owner; + spender; dummy; - return ""; + return 0; } - /// @dev EVM selector for this function is: 0x95d89b41, - /// or in textual repr: symbol() - function symbol() public view returns (string memory) { + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address spender, uint256 amount) public returns (bool) { require(false, stub_error); - dummy; - return ""; + spender; + amount; + dummy = 0; + return false; } - /// @dev EVM selector for this function is: 0x18160ddd, - /// or in textual repr: totalSupply() - function totalSupply() public view returns (uint256) { + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) public view returns (uint256) { require(false, stub_error); + owner; dummy; return 0; } @@ -57,11 +96,26 @@ return 0; } - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) public view returns (uint256) { + /// @dev EVM selector for this function is: 0x06fdde03, + /// or in textual repr: name() + function name() public view returns (string memory) { + require(false, stub_error); + dummy; + return ""; + } + + /// @dev EVM selector for this function is: 0x95d89b41, + /// or in textual repr: symbol() + function symbol() public view returns (string memory) { + require(false, stub_error); + dummy; + return ""; + } + + /// @dev EVM selector for this function is: 0x18160ddd, + /// or in textual repr: totalSupply() + function totalSupply() public view returns (uint256) { require(false, stub_error); - owner; dummy; return 0; } @@ -90,26 +144,6 @@ dummy = 0; return false; } - - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address spender, uint256 amount) public returns (bool) { - require(false, stub_error); - spender; - amount; - dummy = 0; - return false; - } - - /// @dev EVM selector for this function is: 0xdd62ed3e, - /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) public view returns (uint256) { - require(false, stub_error); - owner; - spender; - dummy; - return 0; - } } -contract UniqueNativeFungible is Dummy, ERC165, ERC20 {} +contract UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {} --- a/tests/src/eth/abi/nativeFungible.json +++ b/tests/src/eth/abi/nativeFungible.json @@ -127,6 +127,24 @@ }, { "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } @@ -135,5 +153,32 @@ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "to", + "type": "tuple" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFromCross", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" } ] --- a/tests/src/eth/api/UniqueNativeFungible.sol +++ b/tests/src/eth/api/UniqueNativeFungible.sol @@ -12,6 +12,27 @@ function supportsInterface(bytes4 interfaceID) external view returns (bool); } +/// @dev the ERC-165 identifier for this interface is 0xff15c6f4 +interface ERC20UniqueExtensions is Dummy, ERC165 { + /// @dev EVM selector for this function is: 0x2ada85ff, + /// or in textual repr: transferCross((address,uint256),uint256) + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0xd5cf430b, + /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) + function transferFromCross( + CrossAddress memory from, + CrossAddress memory to, + uint256 amount + ) external returns (bool); +} + +/// Cross account struct +struct CrossAddress { + address eth; + uint256 sub; +} + /// @dev inlined interface interface ERC20Events { event Transfer(address indexed from, address indexed to, uint256 value); @@ -20,6 +41,22 @@ /// @dev the ERC-165 identifier for this interface is 0x942e8b22 interface ERC20 is Dummy, ERC165, ERC20Events { + /// @dev EVM selector for this function is: 0xdd62ed3e, + /// or in textual repr: allowance(address,address) + function allowance(address owner, address spender) external view returns (uint256); + + /// @dev EVM selector for this function is: 0x095ea7b3, + /// or in textual repr: approve(address,uint256) + function approve(address spender, uint256 amount) external returns (bool); + + /// @dev EVM selector for this function is: 0x70a08231, + /// or in textual repr: balanceOf(address) + function balanceOf(address owner) external view returns (uint256); + + /// @dev EVM selector for this function is: 0x313ce567, + /// or in textual repr: decimals() + function decimals() external view returns (uint8); + /// @dev EVM selector for this function is: 0x06fdde03, /// or in textual repr: name() function name() external view returns (string memory); @@ -32,14 +69,6 @@ /// or in textual repr: totalSupply() function totalSupply() external view returns (uint256); - /// @dev EVM selector for this function is: 0x313ce567, - /// or in textual repr: decimals() - function decimals() external view returns (uint8); - - /// @dev EVM selector for this function is: 0x70a08231, - /// or in textual repr: balanceOf(address) - function balanceOf(address owner) external view returns (uint256); - /// @dev EVM selector for this function is: 0xa9059cbb, /// or in textual repr: transfer(address,uint256) function transfer(address to, uint256 amount) external returns (bool); @@ -51,14 +80,6 @@ address to, uint256 amount ) external returns (bool); - - /// @dev EVM selector for this function is: 0x095ea7b3, - /// or in textual repr: approve(address,uint256) - function approve(address spender, uint256 amount) external returns (bool); - - /// @dev EVM selector for this function is: 0xdd62ed3e, - /// or in textual repr: allowance(address,address) - function allowance(address owner, address spender) external view returns (uint256); } -interface UniqueNativeFungible is Dummy, ERC165, ERC20 {} +interface UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {}