Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Silo to silo transfers #225

Merged
merged 51 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3e42c1b
Implement silo to silo transfers
karim-en Jul 21, 2023
1489933
Silo2Silo: Pass deposit amount on register token
karim-en Aug 8, 2023
be4dea6
Silo to silo tests (#226)
olga24912 Aug 10, 2023
c6f793f
Make `registerToken` trustless
karim-en Aug 11, 2023
9a43fc1
Fix integration tests project
karim-en Aug 14, 2023
86a8fce
Call without wNEAR transfer & minor code improvments
karim-en Aug 16, 2023
92a4995
Replace uint256 with u128
karim-en Aug 17, 2023
69796e7
Add UUPS proxy
karim-en Aug 17, 2023
301b031
Fix naming
karim-en Aug 17, 2023
2c64fdc
Add upgrade script & minor code improvments
karim-en Aug 17, 2023
e2eb56e
Fix tests
karim-en Aug 17, 2023
866c771
remove redundant values from libs and allow_paths
olga24912 Aug 24, 2023
44aeb36
Apply suggestions from code review
karim-en Sep 6, 2023
699e8a8
Check if the storage already registered
karim-en Sep 6, 2023
1ce0d4d
Minor code improvment
karim-en Sep 10, 2023
f87f955
Fix test
karim-en Sep 10, 2023
1e58cff
Use `ONE_NEAR`
karim-en Sep 10, 2023
68f3109
Fix variable naming
karim-en Sep 11, 2023
4bc4e99
Add more events & minor improvments
karim-en Sep 15, 2023
3dedaab
Withdraw: decrease the balance in main call
karim-en Sep 15, 2023
5c91b1c
Improve `withdrawCallback`
karim-en Sep 20, 2023
da5713c
Remove comment
karim-en Sep 20, 2023
7548ad3
Add `withdrawTo` function
karim-en Sep 20, 2023
312d8e7
Add pause/unpause logic
karim-en Sep 20, 2023
95a1b89
Emit event on initiating a ft transfer call
karim-en Sep 20, 2023
c752ebb
Remove apostrophe to fix git formating
karim-en Sep 21, 2023
f3fa4c2
Remove apostrophe to fix git formating
karim-en Sep 21, 2023
9b64b59
Add possibility to register recipients
karim-en Sep 25, 2023
b5ceaba
Fix test
karim-en Sep 25, 2023
8cece50
Add `sender` to the transfers events
karim-en Sep 25, 2023
5263f9e
Add `isRecipientStorageRegistered`
karim-en Sep 26, 2023
82c51e2
Index `nonce` instead of `receiverId`
karim-en Sep 27, 2023
1f44131
Add amount to the event
karim-en Sep 27, 2023
f73b503
Add support for native tokens
karim-en Sep 27, 2023
9f6e148
Update deploy script
karim-en Sep 27, 2023
5d58802
Merge branch 'master' into silo-to-silo
karim-en Oct 12, 2023
7803719
Update toolchain
karim-en Oct 12, 2023
d4d939d
Add extra check for attached value
karim-en Oct 12, 2023
c4b54bd
Silo-to-silo: extra tests (#229)
olga24912 Oct 12, 2023
7c9a946
Silo-to-silo: scripts for interaction with SiloToSilo contract (#230)
olga24912 Oct 12, 2023
abc94c9
Revert toolchain update
karim-en Oct 12, 2023
cb8ff91
Transfer wNEAR on storage deposit
karim-en Dec 11, 2023
4a0076d
Use proxy address from the config
karim-en Dec 12, 2023
bd9892b
Apply fmt
karim-en Dec 12, 2023
f6fc53a
Remove deprecated code
karim-en Dec 12, 2023
66042ba
Deploy on enpower silo
karim-en Dec 20, 2023
49aaa09
Fix chainId
karim-en Dec 21, 2023
ab1c436
Panic if the transfer failed
karim-en Jan 12, 2024
1cce40a
Upgrade on enpower silo
karim-en Jan 12, 2024
be85252
Re-upgrade contract on mainnet & enpower silo
karim-en Jan 12, 2024
aee049a
Fix withdraw test
karim-en Jan 12, 2024
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: 4 additions & 0 deletions silo-to-silo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
artifacts/
cache/
.env
14 changes: 14 additions & 0 deletions silo-to-silo/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false
}
}
]
}
8 changes: 8 additions & 0 deletions silo-to-silo/contracts/IEvmErc20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IEvmErc20 is IERC20 {
function withdrawToNear(bytes memory recipient, uint256 amount) external;
}
182 changes: 182 additions & 0 deletions silo-to-silo/contracts/SiloToSilo.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;

import {IERC20 as IERC20_NEAR} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AuroraSdk, NEAR, PromiseCreateArgs, PromiseResultStatus, PromiseWithCallback} from "@auroraisnear/aurora-sdk/aurora-sdk/AuroraSdk.sol";
import "@auroraisnear/aurora-sdk/aurora-sdk/Utils.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./IEvmErc20.sol";

contract SiloToSilo is AccessControl {
olga24912 marked this conversation as resolved.
Show resolved Hide resolved
using AuroraSdk for NEAR;
using AuroraSdk for PromiseCreateArgs;
using AuroraSdk for PromiseWithCallback;

bytes32 public constant CALLBACK_ROLE = keccak256("CALLBACK_ROLE");

uint64 constant BASE_NEAR_GAS = 10_000_000_000_000;
uint64 constant WITHDRAW_NEAR_GAS = 50_000_000_000_000;
uint64 constant FT_TRANSFER_CALL_NEAR_GAS = 200_000_000_000_000;

uint128 constant NEAR_STORAGE_DEPOSIT = 12_500_000_000_000_000_000_000;
olga24912 marked this conversation as resolved.
Show resolved Hide resolved

NEAR public near;
string siloAccountId;

//[auroraErc20Token] => tokenAccountIdOnNear
mapping(IEvmErc20 => string) registeredTokens;

//[auroraErc20Token][userAddressOnAurora] => userBalance
mapping(IEvmErc20 => mapping(address => uint128)) balance;

event TokenRegistered(IEvmErc20 token, string nearAccountId);

constructor(address wnearAddress, string memory _siloAccountId) {
near = AuroraSdk.initNear(IERC20_NEAR(wnearAddress));
siloAccountId = _siloAccountId;

_grantRole(CALLBACK_ROLE, AuroraSdk.nearRepresentitiveImplicitAddress(address(this)));
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

// TODO: make it trustless
function registerToken(IEvmErc20 token, string memory nearTokenAccountId) public onlyRole(DEFAULT_ADMIN_ROLE) {
near.wNEAR.transferFrom(msg.sender, address(this), uint256(NEAR_STORAGE_DEPOSIT));
bytes memory args = bytes(
string.concat('{"account_id": "', getNearAccountId(), '", "registration_only": true }')
);

PromiseCreateArgs memory callStorageDeposit = near.call(
nearTokenAccountId,
"storage_deposit",
args,
NEAR_STORAGE_DEPOSIT,
BASE_NEAR_GAS
);
callStorageDeposit.transact();

registeredTokens[token] = nearTokenAccountId;
olga24912 marked this conversation as resolved.
Show resolved Hide resolved
emit TokenRegistered(token, nearTokenAccountId);
}

function ftTransferCallToNear(
IEvmErc20 token,
uint128 amount,
string calldata receiverId,
string calldata message
) external {
karim-en marked this conversation as resolved.
Show resolved Hide resolved
string storage tokenAccountId = registeredTokens[token];
require(address(token) != address(0), "The token is not registered!");

token.transferFrom(msg.sender, address(this), amount);
// WARNING: The `withdrawToNear` method works asynchronously.
// As a result, there is no guarantee that this method will be completed before `initTransfer`.
// In case of such an error, the user will be able to call `withdraw` method and get his/her tokens back.
// We expect such an error not to happen as long as transactions were executed in one shard.
token.withdrawToNear(bytes(getNearAccountId()), amount);
olga24912 marked this conversation as resolved.
Show resolved Hide resolved

bytes memory args = bytes(
string.concat(
'{"receiver_id": "',
receiverId,
'", "amount": "',
Strings.toString(amount),
'", "msg": "',
message,
'"}'
)
);

PromiseCreateArgs memory callFtTransfer = near.call(
tokenAccountId,
"ft_transfer_call",
args,
1,
FT_TRANSFER_CALL_NEAR_GAS
);
bytes memory callbackArg = abi.encodeWithSelector(this.ftTransferCallCallback.selector, msg.sender, amount);
PromiseCreateArgs memory callback = near.auroraCall(address(this), callbackArg, 0, BASE_NEAR_GAS);

callFtTransfer.then(callback).transact();
}

function ftTransferCallCallback(address sender, IEvmErc20 token, uint128 amount) public onlyRole(CALLBACK_ROLE) {
uint128 transferredAmount = 0;

if (AuroraSdk.promiseResult(0).status == PromiseResultStatus.Successful) {
transferredAmount = _stringToUint(AuroraSdk.promiseResult(0).output);
}

balance[token][sender] += (amount - transferredAmount);
}

function withdraw(IEvmErc20 token) public {
string storage tokenAccountId = registeredTokens[token];
uint128 senderBalance = balance[token][msg.sender];

require(senderBalance > 0, "The signer token balance = 0");

near.wNEAR.transferFrom(msg.sender, address(this), uint256(1));
bytes memory args = bytes(
string.concat(
'{"receiver_id":',
siloAccountId,
'"amount": "',
Strings.toString(senderBalance),
'", "msg": "',
_addressToString(msg.sender),
'"}'
)
);
PromiseCreateArgs memory callWithdraw = near.call(
tokenAccountId,
"ft_transfer_call",
args,
1,
WITHDRAW_NEAR_GAS
);
bytes memory callbackArg = abi.encodeWithSelector(this.withdrawCallback.selector, msg.sender, token);
PromiseCreateArgs memory callback = near.auroraCall(address(this), callbackArg, 0, BASE_NEAR_GAS);

callWithdraw.then(callback).transact();
}

function withdrawCallback(address sender, IEvmErc20 token) public onlyRole(CALLBACK_ROLE) {
require(
AuroraSdk.promiseResult(0).status == PromiseResultStatus.Successful,
"ERROR: The `Withdraw` XCC is fail"
);

uint128 transferredAmount = _stringToUint(AuroraSdk.promiseResult(0).output);

if (transferredAmount > 0) {
balance[token][sender] -= transferredAmount;
}
}

function getNearAccountId() public view returns (string memory) {
return string.concat(_addressToString(address(this)), ".", siloAccountId);
}

function getTokenAccountId(IEvmErc20 token) public view returns (string memory) {
return registeredTokens[token];
}

function getUserBalance(IEvmErc20 token, address userAddress) public view returns (uint128) {
return balance[token][userAddress];
}

function _addressToString(address auroraAddress) private pure returns (string memory) {
return Utils.bytesToHex(abi.encodePacked(auroraAddress));
}

function _stringToUint(bytes memory b) private pure returns (uint128) {
uint128 result = 0;
for (uint256 i = 0; i < b.length; i++) {
result = result * 10 + (uint128(uint8(b[i])) - 48);
}
return result;
}
}
30 changes: 30 additions & 0 deletions silo-to-silo/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require('dotenv').config();

const AURORA_PRIVATE_KEY = process.env.AURORA_PRIVATE_KEY;

module.exports = {
solidity: {
version: "0.8.17",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
testnet_aurora: {
url: 'https://testnet.aurora.dev',
accounts: [`0x${AURORA_PRIVATE_KEY}`],
chainId: 1313161555
},
develop_aurora: {
url: 'https://develop.rpc.testnet.aurora.dev:8545',
accounts: [`0x${AURORA_PRIVATE_KEY}`]
},
},
mocha: {
timeout: 100000000
}
};

14 changes: 14 additions & 0 deletions silo-to-silo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "hardhat-project",
"license": "MIT",
"devDependencies": {
"hardhat": "^2.17",
"prettier": "^3",
"prettier-plugin-solidity": "^1.1.3"
},
"dependencies": {
"@auroraisnear/aurora-sdk": "^0.0.1",
"@openzeppelin/contracts": "^4.8.1",
"dotenv": "^16"
}
}
Loading
Loading