Skip to content

Commit

Permalink
support transferToAccountId32 for Tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
wangjj9219 committed Nov 1, 2023
1 parent a10f8c8 commit 688bef4
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 154 deletions.
2 changes: 2 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
nodeLinker: node-modules

npmRegistryServer: "https://registry.npmjs.org/"

yarnPath: .yarn/releases/yarn-3.6.0.cjs
41 changes: 41 additions & 0 deletions contracts/docs/token/Token.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,29 @@ Moves `amount` tokens from `from` to `to` using the allowance mechanism. `amount
|---|---|---|
| _0 | bool | Returns a boolean value indicating whether the operation succeeded. |

### transferToAccountId32

```solidity
function transferToAccountId32(bytes32 dest, uint256 amount) external nonpayable returns (bool)
```

Moves `amount` tokens from the caller's account to `dest`, which is AccountId32 type account.

*It'll emit an {TransferToAccountId32} event. The caller must have a balance of at least `amount`.*

#### Parameters

| Name | Type | Description |
|---|---|---|
| dest | bytes32 | The dest AccountId32 type account, it cannot be the zero AccountId32. |
| amount | uint256 | The transfer amount. |

#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | bool | Returns a boolean value indicating whether the operation succeeded. |



## Events
Expand Down Expand Up @@ -279,5 +302,23 @@ event Transfer(address indexed from, address indexed to, uint256 value)
| to `indexed` | address | undefined |
| value | uint256 | undefined |

### TransferToAccountId32

```solidity
event TransferToAccountId32(address sender, bytes32 dest, uint256 amount)
```

Transfer event to AccountId32 type account.

*This is Transfer event which transfer AccountId32 type account.*

#### Parameters

| Name | Type | Description |
|---|---|---|
| sender | address | The sender of the transaction. |
| dest | bytes32 | The dest AccountId32 type account. |
| amount | uint256 | The transfer amount. |



69 changes: 23 additions & 46 deletions contracts/token/MultiCurrency.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,91 +8,68 @@ pragma solidity ^0.8.0;
/// @dev This contracts will interact with currencies pallet
library MultiCurrency {
/// @dev The MultiCurrency precompile address.
address private constant PRECOMPILE =
address(0x0000000000000000000000000000000000000400);
address private constant PRECOMPILE = address(0x0000000000000000000000000000000000000400);

function name() internal view returns (string memory) {
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(
abi.encodeWithSignature("name()")
);
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(abi.encodeWithSignature("name()"));
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}

return abi.decode(returnData, (string));
}

function symbol() internal view returns (string memory) {
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(
abi.encodeWithSignature("symbol()")
);
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(abi.encodeWithSignature("symbol()"));
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}

return abi.decode(returnData, (string));
}

function decimals() internal view returns (uint8) {
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(
abi.encodeWithSignature("decimals()")
);
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(abi.encodeWithSignature("decimals()"));
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}

return abi.decode(returnData, (uint8));
}

function totalSupply() internal view returns (uint256) {
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(
abi.encodeWithSignature("totalSupply()")
);
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(abi.encodeWithSignature("totalSupply()"));
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}

return abi.decode(returnData, (uint256));
}

function balanceOf(address account) internal view returns (uint256) {
(bool success, bytes memory returnData) = PRECOMPILE.staticcall(
abi.encodeWithSignature("balanceOf(address)", account)
);
(bool success, bytes memory returnData) =
PRECOMPILE.staticcall(abi.encodeWithSignature("balanceOf(address)", account));
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}

return abi.decode(returnData, (uint256));
}

function transfer(
address sender,
address recipient,
uint256 amount
) internal {
function transfer(address sender, address recipient, uint256 amount) internal {
(bool success, bytes memory returnData) =
PRECOMPILE.call(abi.encodeWithSignature("transfer(address,address,uint256)", sender, recipient, amount));
assembly {
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}
}

function transferToAccountId(address sender, bytes32 recipient, uint256 amount) internal {
(bool success, bytes memory returnData) = PRECOMPILE.call(
abi.encodeWithSignature(
"transfer(address,address,uint256)",
sender,
recipient,
amount
)
abi.encodeWithSignature("transferToAccountId(address,bytes32,uint256)", sender, recipient, amount)
);
assembly {
if eq(success, 0) {
revert(add(returnData, 0x20), returndatasize())
}
if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) }
}
}
}
73 changes: 35 additions & 38 deletions contracts/token/Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ contract Token is IERC20 {

mapping(address => mapping(address => uint256)) private _allowances;

/// @notice Transfer event to AccountId32 type account.
/// @param sender The sender of the transaction.
/// @param dest The dest AccountId32 type account.
/// @param amount The transfer amount.
/// @dev This is Transfer event which transfer AccountId32 type account.
event TransferToAccountId32(address sender, bytes32 dest, uint256 amount);

/// @notice Get the name of the token.
/// @return Returns the name of the token.
function name() public view returns (string memory) {
Expand Down Expand Up @@ -55,10 +62,7 @@ contract Token is IERC20 {
/// @param to The dest address, it cannot be the zero address.
/// @param amount The transfer amount.
/// @return Returns a boolean value indicating whether the operation succeeded.
function transfer(
address to,
uint256 amount
) public override returns (bool) {
function transfer(address to, uint256 amount) public override returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
Expand All @@ -69,10 +73,7 @@ contract Token is IERC20 {
/// @param spender The spender address.
/// @return Returns the remaining number of tokens.
/// The `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default.
function allowance(
address owner,
address spender
) public view override returns (uint256) {
function allowance(address owner, address spender) public view override returns (uint256) {
return _allowances[owner][spender];
}

Expand All @@ -83,10 +84,7 @@ contract Token is IERC20 {
/// @param spender Approve the spender.
/// @param amount The approve amount.
/// @return Returns a boolean value indicating whether the operation succeeded.
function approve(
address spender,
uint256 amount
) public override returns (bool) {
function approve(address spender, uint256 amount) public override returns (bool) {
address owner = msg.sender;
_approve(owner, spender, amount);
return true;
Expand All @@ -100,11 +98,7 @@ contract Token is IERC20 {
/// @param to Transfer amount to the address. It cannot be the zero address.
/// @param amount The transfer amount.
/// @return Returns a boolean value indicating whether the operation succeeded.
function transferFrom(
address from,
address to,
uint256 amount
) public override returns (bool) {
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
address spender = msg.sender;
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
Expand All @@ -117,10 +111,7 @@ contract Token is IERC20 {
/// @param spender It cannot be the zero address.
/// @param addedValue The added value.
/// @return Returns a boolean value indicating whether the operation succeeded.
function increaseAllowance(
address spender,
uint256 addedValue
) public returns (bool) {
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
address owner = msg.sender;
_approve(owner, spender, _allowances[owner][spender] + addedValue);
return true;
Expand All @@ -132,23 +123,36 @@ contract Token is IERC20 {
/// @param spender must have allowance for the caller of at least `subtractedValue`. It cannot be the zero address.
/// @param subtractedValue The subtracted value.
/// @return Returns a boolean value indicating whether the operation succeeded.
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public returns (bool) {
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
address owner = msg.sender;
uint256 currentAllowance = _allowances[owner][spender];
require(
currentAllowance >= subtractedValue,
"ERC20: decreased allowance below zero"
);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}

return true;
}

/// @notice Moves `amount` tokens from the caller's account to `dest`, which is AccountId32 type account.
/// @dev It'll emit an {TransferToAccountId32} event. The caller must have a balance of at least `amount`.
/// @param dest The dest AccountId32 type account, it cannot be the zero AccountId32.
/// @param amount The transfer amount.
/// @return Returns a boolean value indicating whether the operation succeeded.
function transferToAccountId32(bytes32 dest, uint256 amount) public returns (bool) {
address from = msg.sender;
require(from != address(0), "ERC20: transfer from the zero address");
require(
dest != 0x0000000000000000000000000000000000000000000000000000000000000000,
"ERC20: transfer to the zero AccountId32"
);

MultiCurrency.transferToAccountId(from, dest, amount);
emit TransferToAccountId32(from, dest, amount);

return true;
}

/// @dev Moves `amount` of tokens from `sender` to `recipient`.
/// This internal function is equivalent to {transfer}, and can be used to.
/// e.g. implement automatic token fees, slashing mechanisms, etc.
Expand Down Expand Up @@ -182,17 +186,10 @@ contract Token is IERC20 {
/// Does not update the allowance amount in case of infinite allowance.
/// Revert if not enough allowance is available.
/// Might emit an {Approval} event.
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal {
function _spendAllowance(address owner, address spender, uint256 amount) internal {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(
currentAllowance >= amount,
"ERC20: insufficient allowance"
);
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
Expand Down
Loading

0 comments on commit 688bef4

Please sign in to comment.