ERC20原子互换
我的目标是创建一个简单的以太坊智能合约,可以在令牌之间进行原子交换。最初,这应该是跨链交换,但我正在努力做基础知识。 我正在使用使用OpenZeppelin ERC20.SOL和IERC20.SOL合同并使用Remix-IDE创建合同的ERC20令牌。
当我尝试转移令牌或使用索赔()
函数时,我会遇到诸如
'erc20:转移金额超过余额'
'erc20:不足津贴'
必须大于0'
的错误,“津贴” 即使在Alicecoin中使用anderve()
函数之后,出现错误,而Bobcoin合同
一些问题:
答:我需要在合同中使用它之前分别部署令牌?
B:所有ERC20功能是否应该与我的原子交换合同均签订同一合同?
C:我为所有者,收件人和代币地址创建了ETH地址。但是我如何利用合同中给我的混音IDE地址?
任何帮助都非常感谢
:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.1;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract AliceCoin is ERC20 {
constructor(uint256 supply) ERC20("AliceCoin", "ALI") {
_mint(msg.sender, supply);
}
}
contract BobCoin is ERC20{
constructor(uint256 supply) ERC20("BobCoin", "BOB"){
_mint(msg.sender, supply);
}
}
contract AtomicSwap{
ERC20 public tokenA;
ERC20 public tokenB;
// A constructor for the smart contract
constructor() payable {
//_owner = payable(msg.sender);
//_recipient = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
tokenA = new AliceCoin(100000);
tokenB = new BobCoin(100000);
}
/*
Attributes of the Atomic Swap
-- Timelock
-- Hashlock
-- Address transfer from
-- Address transfer to
-- secret key
-- Token | That would use functions from the interface
--
*/
struct Swap {
// User who should recieve the contract
address payable recipient;
// Owner of the coin
address payable Owner;
// Address of the token
address tokenAddress;
// The amount of the tokens swapped
uint256 amount;
//uint256 amount; // An amount of the token wanting to be transferred
// Time stated for Swap to be executed
uint256 timelock;
// Cryptographic secret key
bytes32 Hashlock; //0xd218ef7a2461c961fdd5c0cd5a547f52b863d14db3e08e55f365e4cd0b4333c5;
// Secret key
string secret;
// Boolean to check if the owner has been refunded
bool refunded;
// Boolean to check if the token has been claimed
bool claimed;
}
mapping(address => mapping(address => uint256)) private _allowances;
mapping(bytes32 => Swap) public swaps;
event NewAtomicSwap(
bytes32 swapId,
address payable Owner,
address payable recipient,
address tokenAddress,
uint256 amount,
bytes32 Hashlock,
uint256 timelock
);
event Claimed(
bytes32 swapId
);
event Refunded(
bytes32 swapId
);
// Modifiers
modifier checkAllowance(address _token, address _Owner, uint256 _amount){
require(_amount > 0, "Token amount must be greater than 0");
require(
ERC20(_token).allowance(_Owner, address(this)) >= _amount,
"Allowance must be greater than 0"
);
_;
}
modifier futureTimelock(uint256 _time){
require(_time > block.timestamp, "timelock has to be set in the future");
_;
}
modifier claimable(bytes32 _swapId) {
require(swaps[_swapId].recipient == msg.sender, "This is not the right recipient");
require(swaps[_swapId].claimed == false, "already claimed");
require(swaps[_swapId].refunded == false, "already refunded");
_;
}
modifier matchingHashlocks(bytes32 _swapId, bytes32 _x){
require(
swaps[_swapId].Hashlock == keccak256(abi.encodePacked(_x)),
"incorrect hashlock"
);
_;
}
modifier existingContract(bytes32 _swapId) {
require(haveContract(_swapId), "contract does not exist");
_;
}
modifier refundable(bytes32 _swapId) {
require(swaps[_swapId].Owner == msg.sender, "Only the sender of this coin can refund");
require(swaps[_swapId].refunded == false, "Already refunded");
require(swaps[_swapId].claimed == false, "Already claimed");
require(swaps[_swapId].timelock <= block.timestamp, "Timelock not yet passed");
_;
}
function newSwap(
address payable _recipient,
bytes32 _Hashlock,
uint256 _timelock,
address _tokenAddress,
uint256 _amount
)
public // Visibility
payable
checkAllowance(_tokenAddress, msg.sender, _amount)
futureTimelock(_timelock)
returns(bytes32 swapId)
{
swapId = keccak256(
abi.encodePacked(
msg.sender,
_recipient,
_tokenAddress,
_amount,
_Hashlock,
_timelock
)
);
if(haveContract(swapId))
revert("Contract exists");
if(!AliceCoin(_tokenAddress).transfer(_recipient , _amount))
revert("transfer failed");
swaps[swapId] = Swap({
recipient : _recipient,
Owner : payable(msg.sender),
tokenAddress : _tokenAddress,
amount : msg.value,
timelock : getTimestamp() + 60000,
Hashlock : _Hashlock,
secret : "djkcoeuxhjkdf",
Open : false,
locked : false,
finished : false,
refunded : false,
claimed: false
});
emit NewAtomicSwap(
swapId,
payable(msg.sender),
_recipient,
_tokenAddress,
_amount,
_Hashlock,
_timelock
);
}
/* Function for recipient to claim token */
/* Only be claimed if Owner has opened the swap with _____ */
function claim(bytes32 _swapId, bytes32 _Hashlock)
public
payable
claimable(_swapId)
matchingHashlocks(_swapId, _Hashlock)
existingContract(_swapId)
returns (bool)
{
Swap storage s = swaps[_swapId];
s.Hashlock = _Hashlock;
s.claimed = true;
AliceCoin(s.tokenAddress).transfer(s.recipient, s.amount);
emit Claimed(_swapId);
return true;
}
function refund(bytes32 _swapId)
external
existingContract(_swapId)
refundable(_swapId)
returns (bool)
{
Swap storage s = swaps[_swapId];
s.refunded = true;
AliceCoin(s.tokenAddress).transfer(s.Owner, s.amount);
emit Refunded(_swapId);
return true;
}
function haveContract(bytes32 _swapId)
internal
view
returns (bool available)
{
available = (swaps[_swapId].Owner != address(0));
}
My goal is to create a simple Ethereum smart contract that allows for an atomic swap between tokens. Initially it should've been a cross-chain swap but I am struggling to do the basics.
I am using ERC20 tokens that i have created using the openzeppelin ERC20.sol and IERC20.sol contracts and using Remix-IDE to create the contracts.
When i try to transfer the tokens or use the claim()
function, I encounter errors such as
' ERC20: transfer amount exceeds balance'
' ERC20: insufficient allowance'
'Allowance must be greater than 0'
These errors occur even after i use the approve()
function in the AliceCoin and BobCoin contracts
Some Questions:
A: Do i need to deploy the tokens separately before i can use it in my contract?
B: Should all the ERC20 functions be in the same contract as my atomic swap contract?
C: I have create ETH addresses for owner, recipient and token address. But how to i utilize the Remix IDE addresses given to me in my contract?
Any help is much appreciated
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.1;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract AliceCoin is ERC20 {
constructor(uint256 supply) ERC20("AliceCoin", "ALI") {
_mint(msg.sender, supply);
}
}
contract BobCoin is ERC20{
constructor(uint256 supply) ERC20("BobCoin", "BOB"){
_mint(msg.sender, supply);
}
}
contract AtomicSwap{
ERC20 public tokenA;
ERC20 public tokenB;
// A constructor for the smart contract
constructor() payable {
//_owner = payable(msg.sender);
//_recipient = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
tokenA = new AliceCoin(100000);
tokenB = new BobCoin(100000);
}
/*
Attributes of the Atomic Swap
-- Timelock
-- Hashlock
-- Address transfer from
-- Address transfer to
-- secret key
-- Token | That would use functions from the interface
--
*/
struct Swap {
// User who should recieve the contract
address payable recipient;
// Owner of the coin
address payable Owner;
// Address of the token
address tokenAddress;
// The amount of the tokens swapped
uint256 amount;
//uint256 amount; // An amount of the token wanting to be transferred
// Time stated for Swap to be executed
uint256 timelock;
// Cryptographic secret key
bytes32 Hashlock; //0xd218ef7a2461c961fdd5c0cd5a547f52b863d14db3e08e55f365e4cd0b4333c5;
// Secret key
string secret;
// Boolean to check if the owner has been refunded
bool refunded;
// Boolean to check if the token has been claimed
bool claimed;
}
mapping(address => mapping(address => uint256)) private _allowances;
mapping(bytes32 => Swap) public swaps;
event NewAtomicSwap(
bytes32 swapId,
address payable Owner,
address payable recipient,
address tokenAddress,
uint256 amount,
bytes32 Hashlock,
uint256 timelock
);
event Claimed(
bytes32 swapId
);
event Refunded(
bytes32 swapId
);
// Modifiers
modifier checkAllowance(address _token, address _Owner, uint256 _amount){
require(_amount > 0, "Token amount must be greater than 0");
require(
ERC20(_token).allowance(_Owner, address(this)) >= _amount,
"Allowance must be greater than 0"
);
_;
}
modifier futureTimelock(uint256 _time){
require(_time > block.timestamp, "timelock has to be set in the future");
_;
}
modifier claimable(bytes32 _swapId) {
require(swaps[_swapId].recipient == msg.sender, "This is not the right recipient");
require(swaps[_swapId].claimed == false, "already claimed");
require(swaps[_swapId].refunded == false, "already refunded");
_;
}
modifier matchingHashlocks(bytes32 _swapId, bytes32 _x){
require(
swaps[_swapId].Hashlock == keccak256(abi.encodePacked(_x)),
"incorrect hashlock"
);
_;
}
modifier existingContract(bytes32 _swapId) {
require(haveContract(_swapId), "contract does not exist");
_;
}
modifier refundable(bytes32 _swapId) {
require(swaps[_swapId].Owner == msg.sender, "Only the sender of this coin can refund");
require(swaps[_swapId].refunded == false, "Already refunded");
require(swaps[_swapId].claimed == false, "Already claimed");
require(swaps[_swapId].timelock <= block.timestamp, "Timelock not yet passed");
_;
}
function newSwap(
address payable _recipient,
bytes32 _Hashlock,
uint256 _timelock,
address _tokenAddress,
uint256 _amount
)
public // Visibility
payable
checkAllowance(_tokenAddress, msg.sender, _amount)
futureTimelock(_timelock)
returns(bytes32 swapId)
{
swapId = keccak256(
abi.encodePacked(
msg.sender,
_recipient,
_tokenAddress,
_amount,
_Hashlock,
_timelock
)
);
if(haveContract(swapId))
revert("Contract exists");
if(!AliceCoin(_tokenAddress).transfer(_recipient , _amount))
revert("transfer failed");
swaps[swapId] = Swap({
recipient : _recipient,
Owner : payable(msg.sender),
tokenAddress : _tokenAddress,
amount : msg.value,
timelock : getTimestamp() + 60000,
Hashlock : _Hashlock,
secret : "djkcoeuxhjkdf",
Open : false,
locked : false,
finished : false,
refunded : false,
claimed: false
});
emit NewAtomicSwap(
swapId,
payable(msg.sender),
_recipient,
_tokenAddress,
_amount,
_Hashlock,
_timelock
);
}
/* Function for recipient to claim token */
/* Only be claimed if Owner has opened the swap with _____ */
function claim(bytes32 _swapId, bytes32 _Hashlock)
public
payable
claimable(_swapId)
matchingHashlocks(_swapId, _Hashlock)
existingContract(_swapId)
returns (bool)
{
Swap storage s = swaps[_swapId];
s.Hashlock = _Hashlock;
s.claimed = true;
AliceCoin(s.tokenAddress).transfer(s.recipient, s.amount);
emit Claimed(_swapId);
return true;
}
function refund(bytes32 _swapId)
external
existingContract(_swapId)
refundable(_swapId)
returns (bool)
{
Swap storage s = swaps[_swapId];
s.refunded = true;
AliceCoin(s.tokenAddress).transfer(s.Owner, s.amount);
emit Refunded(_swapId);
return true;
}
function haveContract(bytes32 _swapId)
internal
view
returns (bool available)
{
available = (swaps[_swapId].Owner != address(0));
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在您的智能合同中,问题是您的两个ERC20令牌(Alicecoin和Bobcoin)的所有者是您的智能联系人。当您这样做时:
与您的ERC20交互的
msg.sender
两个合同本身是智能联系人。这样,AtomicsWap智能合约具有ERC20令牌,它是所有者而不是您的地址!为了解决此问题,您必须以这种方式更改构造函数逻辑:
并且必须首先部署ERC20智能合约,然后再部署
atomicswap
合同,然后将有关ERC20智能合约的地址传达给他的构造函数。另一个问题是
transver
在newswap()
函数中使用。这种移动令牌的方法不包括两个交易,例如:批准+传输因此,在这里您丢弃错误“津贴必须大于0”。要解决此问题,您必须更改此行:与此:
允许您将用户令牌转移到智能合约中。
我对您的原始智能合约进行了一些更改,您可以看到并测试此代码:
In your smart contract the issue is that the owner about your two ERC20 token (AliceCoin and BobCoin) is your smart contact. When you do this:
The
msg.sender
that interact with your ERC20 two contracts is smart contact itself. In this way, AtomicSwap smart contract has the ERC20 tokens and it is the owner and not your address!For resolve this issue, you must change your constructor logic in this way:
And you must to deploy first ERC20 smart contracts and then your
AtomicSwap
contract, and pass into his constructor the address about ERC20 smart contracts.Another issue is the use about
transfer
innewSwap()
function. This method to move tokens don't include two transactions like: approve+transfer consequently here you throw the error 'Allowance must be greater than 0'. To solve this issue you must to change this line:with this:
that allow you to transfer user token into smart contract.
I made some changes to your original smart contract, you can see and test this code: