feat: update SwapCreater.sol claimRelayer to no longer use forwarder (#449)

Co-authored-by: Dmitry Holodov <dimalinux@protonmail.com>
This commit is contained in:
noot
2023-05-01 19:23:17 -04:00
committed by GitHub
parent b3dcb96074
commit fb95751dda
45 changed files with 878 additions and 1126 deletions

File diff suppressed because one or more lines are too long

View File

@@ -4,12 +4,10 @@
package contracts
import (
"bytes"
"context"
"errors"
"testing"
"github.com/athanorlabs/go-relayer/impls/gsnforwarder"
ethcommon "github.com/ethereum/go-ethereum/common"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
@@ -20,73 +18,33 @@ import (
// getContractCode is a test helper that deploys the swap creator contract to read back
// and return the finalised byte code post deployment.
func getContractCode(t *testing.T, forwarderAddr ethcommon.Address) []byte {
func getContractCode(t *testing.T) []byte {
ec, _ := tests.NewEthClient(t)
pk := tests.GetMakerTestKey(t)
contractAddr, _ := deploySwapCreatorWithForwarder(t, ec, pk, forwarderAddr)
contractAddr, _ := deploySwapCreator(t, ec, pk)
code, err := ec.CodeAt(context.Background(), contractAddr, nil)
require.NoError(t, err)
return code
}
func TestCheckForwarderContractCode(t *testing.T) {
ec, _ := tests.NewEthClient(t)
pk := tests.GetMakerTestKey(t)
forwarderAddr := deployForwarder(t, ec, pk)
err := CheckForwarderContractCode(context.Background(), ec, forwarderAddr)
require.NoError(t, err)
}
// This test will fail if the compiled SwapCreator contract is updated, but the
// expectedSwapCreatorBytecodeHex constant is not updated. Use this test to update the
// constant.
func TestExpectedSwapCreatorBytecodeHex(t *testing.T) {
allZeroTrustedForwarder := ethcommon.Address{}
codeHex := ethcommon.Bytes2Hex(getContractCode(t, allZeroTrustedForwarder))
codeHex := ethcommon.Bytes2Hex(getContractCode(t))
require.Equal(t, expectedSwapCreatorBytecodeHex, codeHex,
"update the expectedSwapCreatorBytecodeHex constant with the actual value to fix this test")
}
// This test will fail if the compiled SwapCreator contract is updated, but the
// forwarderAddrIndexes slice of trusted forwarder locations is not updated. Use
// this test to update the slice.
func TestForwarderAddrIndexes(t *testing.T) {
ec, _ := tests.NewEthClient(t)
pk := tests.GetMakerTestKey(t)
forwarderAddr := deployForwarder(t, ec, pk)
contactBytes := getContractCode(t, forwarderAddr)
addressLocations := make([]int, 0) // at the current time, there should always be 2
for i := 0; i < len(contactBytes)-ethAddrByteLen; i++ {
if bytes.Equal(contactBytes[i:i+ethAddrByteLen], forwarderAddr[:]) {
addressLocations = append(addressLocations, i)
i += ethAddrByteLen - 1 // -1 since the loop will increment by 1
}
}
t.Logf("forwarderAddrIndexes: %v", addressLocations)
require.EqualValues(t, forwarderAddrIndices, addressLocations,
"update forwarderAddrIndexes with above logged indexes to fix this test")
}
// Ensure that we correctly verify the SwapCreator contract when initialised with
// different trusted forwarder addresses.
func TestCheckSwapCreatorContractCode(t *testing.T) {
ec, _ := tests.NewEthClient(t)
pk := tests.GetMakerTestKey(t)
forwarderAddrs := []string{
deployForwarder(t, ec, pk).Hex(),
deployForwarder(t, ec, pk).Hex(),
deployForwarder(t, ec, pk).Hex(),
}
for _, addrHex := range forwarderAddrs {
tfAddr := ethcommon.HexToAddress(addrHex)
contractAddr, _ := deploySwapCreatorWithForwarder(t, ec, pk, tfAddr)
parsedTFAddr, err := CheckSwapCreatorContractCode(context.Background(), ec, contractAddr)
require.NoError(t, err)
require.Equal(t, addrHex, parsedTFAddr.Hex())
}
contractAddr, _ := deploySwapCreator(t, ec, pk)
err := CheckSwapCreatorContractCode(context.Background(), ec, contractAddr)
require.NoError(t, err)
}
// Tests that we fail when the wrong contract byte code is found
@@ -94,38 +52,37 @@ func TestCheckSwapCreatorContractCode_fail(t *testing.T) {
ec, _ := tests.NewEthClient(t)
pk := tests.GetMakerTestKey(t)
// Deploy a forwarder contract and then try to verify it as SwapCreator contract
contractAddr := deployForwarder(t, ec, pk)
_, err := CheckSwapCreatorContractCode(context.Background(), ec, contractAddr)
// Deploy a token contract and then try to verify it as SwapCreator contract
contractAddr, _ := deployERC20Token(t, ec, pk, "name", "symbol", 10, 100)
err := CheckSwapCreatorContractCode(context.Background(), ec, contractAddr)
require.ErrorIs(t, err, errInvalidSwapCreatorContract)
}
func TestSepoliaContract(t *testing.T) {
t.Skip("needs to be redeployed before merge")
ctx := context.Background()
ec := tests.NewEthSepoliaClient(t)
// temporarily place a funded sepolia private key below to deploy the test contract
const sepoliaKey = ""
parsedTFAddr, err := CheckSwapCreatorContractCode(ctx, ec, common.StagenetConfig().SwapCreatorAddr)
err := CheckSwapCreatorContractCode(ctx, ec, common.StagenetConfig().SwapCreatorAddr)
if errors.Is(err, errInvalidSwapCreatorContract) && sepoliaKey != "" {
pk, err := ethcrypto.HexToECDSA(sepoliaKey) //nolint:govet // shadow declaration of err
require.NoError(t, err)
forwarderAddr := ethcommon.HexToAddress(gsnforwarder.SepoliaForwarderAddrHex)
swapCreatorAddr, _, err := DeploySwapCreatorWithKey(ctx, ec, pk, forwarderAddr)
swapCreatorAddr, _, err := DeploySwapCreatorWithKey(ctx, ec, pk)
require.NoError(t, err)
t.Fatalf("Update common.StagenetConfig()'s SwapCreatorAddr with %s", swapCreatorAddr.Hex())
}
require.NoError(t, err)
require.Equal(t, gsnforwarder.SepoliaForwarderAddrHex, parsedTFAddr.Hex())
}
func TestMainnetContract(t *testing.T) {
t.Skip("needs to be redeployed before merge")
ctx := context.Background()
ec := tests.NewEthMainnetClient(t)
mainnetConf := common.MainnetConfig()
parsedTFAddr, err := CheckSwapCreatorContractCode(ctx, ec, mainnetConf.SwapCreatorAddr)
err := CheckSwapCreatorContractCode(ctx, ec, mainnetConf.SwapCreatorAddr)
require.NoError(t, err)
require.Equal(t, gsnforwarder.MainnetForwarderAddrHex, parsedTFAddr.Hex())
}

View File

@@ -4,12 +4,12 @@ package contracts
// ever see in a test, so you would need to adjust upwards a little to use as a
// gas limit. We use these values to estimate minimum required balances.
const (
MaxNewSwapETHGas = 50589
MaxNewSwapETHGas = 50639
MaxNewSwapTokenGas = 86218
MaxSetReadyGas = 31872
MaxSetReadyGas = 32054
MaxClaimETHGas = 43349
MaxClaimTokenGas = 47522
MaxRefundETHGas = 43120
MaxRefundTokenGas = 47282
MaxRefundETHGas = 43132
MaxRefundTokenGas = 47294
MaxTokenApproveGas = 47000 // 46223 with our contract
)

View File

@@ -3,6 +3,6 @@ package contracts
// We don't deploy SwapCreator contracts or ERC20 token contracts in swaps, so
// these constants are only compiled in for test files.
const (
maxSwapCreatorDeployGas = 1005177
maxTestERC20DeployGas = 798226 // using long token names or symbols will increase this
maxSwapCreatorDeployGas = 1094089
maxTestERC20DeployGas = 798286 // using long token names or symbols will increase this
)

View File

@@ -1,36 +0,0 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.19;
import {Context} from "./Context.sol";
/**
* @dev Context variant with ERC2771 support.
*/
abstract contract ERC2771Context is Context {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
// TODO: this was modified to be public (is that ok?)
address public immutable _trustedForwarder;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address trustedForwarder) {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}
}

View File

@@ -1,11 +1,10 @@
// SPDX-License-Identifier: LGPLv3
pragma solidity ^0.8.19;
import {ERC2771Context} from "./ERC2771Context.sol";
import {IERC20} from "./IERC20.sol";
import {Secp256k1} from "./Secp256k1.sol";
contract SwapCreator is ERC2771Context, Secp256k1 {
contract SwapCreator is Secp256k1 {
// Swap state is PENDING when the swap is first created and funded
// Alice sets Stage to READY when she sees the funds locked on the other chain.
// this prevents Bob from withdrawing funds without locking funds on the other chain first
@@ -41,6 +40,20 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
uint256 nonce;
}
// RelaySwap contains additional information required for relayed transactions.
// This entire structure is encoded and signed by the swap claimer, and the signature is
// passed to the `claimRelayer` function.
struct RelaySwap {
// the swap the transaction is for
Swap swap;
// the fee, in wei, paid to the relayer
uint256 fee;
// hash of (relayer's payout address || 4-byte salt)
bytes32 relayerHash;
// address of the swap contract this transaction is meant for
address swapCreator;
}
mapping(bytes32 => Stage) public swaps;
event New(
@@ -80,9 +93,6 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
// returned when the caller of `setReady` or `refund` is not the swap owner
error OnlySwapOwner();
// returned when `claimRelayer` is not called by the trusted forwarder
error OnlyTrustedForwarder();
// returned when the signer of the relayed transaction is not the swap's claimer
error OnlySwapClaimer();
@@ -104,7 +114,15 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
// returned when the provided secret does not match the expected public key
error InvalidSecret();
constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {} // solhint-disable-line
// returned when the signature of a `RelaySwap` is invalid
error InvalidSignature();
// returned when the SwapCreator address is a `RelaySwap` is not the addres of this contract
error InvalidContractAddress();
// returned when the hash of the relayer address and salt passed to `claimRelayer`
// does not match the relayer hash in `RelaySwap`
error InvalidRelayerAddress();
// newSwap creates a new Swap instance with the given parameters.
// it returns the swap's ID.
@@ -174,8 +192,9 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
// Bob can claim if:
// - (Alice has set the swap to `ready` or it's past timeout0) and it's before timeout1
function claim(Swap memory _swap, bytes32 _s) public {
_claim(_swap, _s);
function claim(Swap memory _swap, bytes32 _secret) public {
if (msg.sender != _swap.claimer) revert OnlySwapClaimer();
_claim(_swap, _secret);
// send ether to swap claimer
if (_swap.asset == address(0)) {
@@ -190,45 +209,59 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
// Bob can claim if:
// - (Alice has set the swap to `ready` or it's past timeout0) and it's before timeout1
// This function is only callable by the trusted forwarder.
// It transfers the fee to the originator of the transaction.
function claimRelayer(Swap memory _swap, bytes32 _s, uint256 fee) public {
if (!isTrustedForwarder(msg.sender)) revert OnlyTrustedForwarder();
_claim(_swap, _s);
// It transfers the fee to the relayer address specified in `_relaySwap`.
// Note: this function will revert if the swap value is less than the relayer fee;
// in that case, `claim` must be called instead.
function claimRelayer(
RelaySwap memory _relaySwap,
bytes32 _secret,
address payable _relayer,
uint32 _salt,
uint8 v,
bytes32 r,
bytes32 s
) public {
address signer = ecrecover(keccak256(abi.encode(_relaySwap)), v, r, s);
if (signer != _relaySwap.swap.claimer) revert InvalidSignature();
if (address(this) != _relaySwap.swapCreator) revert InvalidContractAddress();
if (keccak256(abi.encodePacked(_relayer, _salt)) != _relaySwap.relayerHash)
revert InvalidRelayerAddress();
_claim(_relaySwap.swap, _secret);
// send ether to swap claimer, subtracting the relayer fee
// which is sent to the originator of the transaction.
// tx.origin is okay here, since it isn't for authentication purposes.
if (_swap.asset == address(0)) {
_swap.claimer.transfer(_swap.value - fee);
payable(tx.origin).transfer(fee); // solhint-disable-line
if (_relaySwap.swap.asset == address(0)) {
_relaySwap.swap.claimer.transfer(_relaySwap.swap.value - _relaySwap.fee);
payable(_relayer).transfer(_relaySwap.fee);
} else {
// WARN: this will FAIL for fee-on-transfer or rebasing tokens if the token
// transfer reverts (i.e. if this contract does not contain _swap.value tokens),
// exposing Bob's secret while giving him nothing.
IERC20(_swap.asset).transfer(_swap.claimer, _swap.value - fee);
IERC20(_swap.asset).transfer(tx.origin, fee); // solhint-disable-line
IERC20(_relaySwap.swap.asset).transfer(
_relaySwap.swap.claimer,
_relaySwap.swap.value - _relaySwap.fee
);
IERC20(_relaySwap.swap.asset).transfer(_relayer, _relaySwap.fee);
}
}
function _claim(Swap memory _swap, bytes32 _s) internal {
function _claim(Swap memory _swap, bytes32 _secret) internal {
bytes32 swapID = keccak256(abi.encode(_swap));
Stage swapStage = swaps[swapID];
if (swapStage == Stage.INVALID) revert InvalidSwap();
if (swapStage == Stage.COMPLETED) revert SwapCompleted();
if (_msgSender() != _swap.claimer) revert OnlySwapClaimer();
if (block.timestamp < _swap.timeout0 && swapStage != Stage.READY) revert TooEarlyToClaim();
if (block.timestamp >= _swap.timeout1) revert TooLateToClaim();
verifySecret(_s, _swap.pubKeyClaim);
emit Claimed(swapID, _s);
verifySecret(_secret, _swap.pubKeyClaim);
emit Claimed(swapID, _secret);
swaps[swapID] = Stage.COMPLETED;
}
// Alice can claim a refund:
// - Until timeout0 unless she calls setReady
// - After timeout1
function refund(Swap memory _swap, bytes32 _s) public {
function refund(Swap memory _swap, bytes32 _secret) public {
bytes32 swapID = keccak256(abi.encode(_swap));
Stage swapStage = swaps[swapID];
if (swapStage == Stage.INVALID) revert InvalidSwap();
@@ -239,8 +272,8 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
(block.timestamp > _swap.timeout0 || swapStage == Stage.READY)
) revert NotTimeToRefund();
verifySecret(_s, _swap.pubKeyRefund);
emit Refunded(swapID, _s);
verifySecret(_secret, _swap.pubKeyRefund);
emit Refunded(swapID, _secret);
// send asset back to swap owner
swaps[swapID] = Stage.COMPLETED;
@@ -251,7 +284,7 @@ contract SwapCreator is ERC2771Context, Secp256k1 {
}
}
function verifySecret(bytes32 _s, bytes32 _hashedPubkey) internal pure {
if (!mulVerify(uint256(_s), uint256(_hashedPubkey))) revert InvalidSecret();
function verifySecret(bytes32 _secret, bytes32 _hashedPubkey) internal pure {
if (!mulVerify(uint256(_secret), uint256(_hashedPubkey))) revert InvalidSecret();
}
}

View File

@@ -8,8 +8,6 @@ import (
"crypto/ecdsa"
"fmt"
"github.com/athanorlabs/go-relayer/common"
"github.com/athanorlabs/go-relayer/impls/gsnforwarder"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
@@ -21,26 +19,19 @@ import (
var log = logging.Logger("contracts")
// DeploySwapCreatorWithKey deploys the SwapCreator contract using the passed privKey to
// pay for the gas.
// pay for the deployment.
func DeploySwapCreatorWithKey(
ctx context.Context,
ec *ethclient.Client,
privKey *ecdsa.PrivateKey,
forwarderAddr ethcommon.Address,
) (ethcommon.Address, *SwapCreator, error) {
txOpts, err := newTXOpts(ctx, ec, privKey)
if err != nil {
return ethcommon.Address{}, nil, err
}
if (forwarderAddr != ethcommon.Address{}) {
if err = registerDomainSeparatorIfNeeded(ctx, ec, privKey, forwarderAddr); err != nil {
return ethcommon.Address{}, nil, fmt.Errorf("failed to deploy swap creator: %w", err)
}
}
log.Infof("deploying SwapCreator.sol with forwarderAddr %s", forwarderAddr)
address, tx, sf, err := DeploySwapCreator(txOpts, ec, forwarderAddr)
log.Infof("deploying SwapCreator.sol")
address, tx, sf, err := DeploySwapCreator(txOpts, ec)
if err != nil {
return ethcommon.Address{}, nil, fmt.Errorf("failed to deploy swap creator: %w", err)
}
@@ -54,109 +45,6 @@ func DeploySwapCreatorWithKey(
return address, sf, nil
}
// DeployGSNForwarderWithKey deploys and registers the GSN forwarder using the passed
// private key to pay the gas fees.
func DeployGSNForwarderWithKey(
ctx context.Context,
ec *ethclient.Client,
privKey *ecdsa.PrivateKey,
) (ethcommon.Address, error) {
txOpts, err := newTXOpts(ctx, ec, privKey)
if err != nil {
return ethcommon.Address{}, err
}
address, tx, contract, err := gsnforwarder.DeployForwarder(txOpts, ec)
if err != nil {
return ethcommon.Address{}, fmt.Errorf("failed to deploy Forwarder.sol: %w", err)
}
_, err = block.WaitForReceipt(ctx, ec, tx.Hash())
if err != nil {
return ethcommon.Address{}, err
}
err = registerDomainSeparator(ctx, ec, privKey, address, contract)
if err != nil {
return ethcommon.Address{}, err
}
return address, nil
}
func isDomainSeparatorRegistered(
ctx context.Context,
ec *ethclient.Client,
forwarderAddr ethcommon.Address,
forwarder *gsnforwarder.Forwarder,
) (isRegistered bool, err error) {
chainID, err := ec.ChainID(ctx)
if err != nil {
return false, err
}
name := gsnforwarder.DefaultName
version := gsnforwarder.DefaultVersion
ds, err := common.GetEIP712DomainSeparator(name, version, chainID, forwarderAddr)
if err != nil {
return false, err
}
opts := &bind.CallOpts{Context: ctx}
return forwarder.Domains(opts, ds)
}
func registerDomainSeparatorIfNeeded(
ctx context.Context,
ec *ethclient.Client,
privKey *ecdsa.PrivateKey,
forwarderAddr ethcommon.Address,
) error {
forwarder, err := gsnforwarder.NewForwarder(forwarderAddr, ec)
if err != nil {
return err
}
isRegistered, err := isDomainSeparatorRegistered(ctx, ec, forwarderAddr, forwarder)
if err != nil {
return err
}
if isRegistered {
return nil
}
return registerDomainSeparator(ctx, ec, privKey, forwarderAddr, forwarder)
}
func registerDomainSeparator(
ctx context.Context,
ec *ethclient.Client,
privKey *ecdsa.PrivateKey,
forwarderAddr ethcommon.Address,
forwarder *gsnforwarder.Forwarder,
) error {
log.Infof("registering domain separator for forwarder %s", forwarderAddr)
txOpts, err := newTXOpts(ctx, ec, privKey)
if err != nil {
return err
}
tx, err := forwarder.RegisterDomainSeparator(txOpts, gsnforwarder.DefaultName, gsnforwarder.DefaultVersion)
if err != nil {
return fmt.Errorf("failed to register domain separator: %w", err)
}
_, err = block.WaitForReceipt(ctx, ec, tx.Hash())
if err != nil {
return err
}
log.Debugf("registered domain separator in forwarder at %s: name=%s version=%s",
forwarderAddr,
gsnforwarder.DefaultName,
gsnforwarder.DefaultVersion,
)
return nil
}
func newTXOpts(ctx context.Context, ec *ethclient.Client, privkey *ecdsa.PrivateKey) (*bind.TransactOpts, error) {
chainID, err := ec.ChainID(ctx)
if err != nil {

View File

@@ -1,38 +0,0 @@
// Copyright 2023 The AthanorLabs/atomic-swap Authors
// SPDX-License-Identifier: LGPL-3.0-only
package contracts
import (
"context"
"testing"
"github.com/athanorlabs/go-relayer/impls/gsnforwarder"
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/tests"
)
func Test_registerDomainSeparatorIfNeeded(t *testing.T) {
ec, _ := tests.NewEthClient(t)
ctx := context.Background()
privKey := tests.GetMakerTestKey(t)
txOpts, err := newTXOpts(ctx, ec, privKey)
require.NoError(t, err)
forwarderAddr, tx, forwarder, err := gsnforwarder.DeployForwarder(txOpts, ec)
require.NoError(t, err)
_ = tests.MineTransaction(t, ec, tx)
isRegistered, err := isDomainSeparatorRegistered(ctx, ec, forwarderAddr, forwarder)
require.NoError(t, err)
require.False(t, isRegistered)
err = registerDomainSeparatorIfNeeded(ctx, ec, privKey, forwarderAddr)
require.NoError(t, err)
isRegistered, err = isDomainSeparatorRegistered(ctx, ec, forwarderAddr, forwarder)
require.NoError(t, err)
require.True(t, isRegistered)
}

View File

@@ -4,81 +4,113 @@
package contracts
import (
"crypto/ecdsa"
"math/big"
"testing"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/common/types"
"github.com/athanorlabs/atomic-swap/tests"
)
func TestSwapCreator_NewSwap_ERC20(t *testing.T) {
pkA := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)
addr := crypto.PubkeyToAddress(pkA.PublicKey)
func deployERC20Token(
t *testing.T,
ec *ethclient.Client,
pk *ecdsa.PrivateKey, // token owner (and pays for deployment)
name string,
symbol string,
decimals uint8,
supplyStdUnits int64,
) (ethcommon.Address, *TestERC20) {
addr := crypto.PubkeyToAddress(pk.PublicKey)
supply := new(big.Int).Mul(big.NewInt(supplyStdUnits),
new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil),
)
// deploy TestERC20
erc20Addr, tx, erc20Contract, err :=
DeployTestERC20(getAuth(t, pkA), ec, "Test of the ERC20 Token", "ERC20Token", 18, addr, big.NewInt(9999))
tokenAddr, tx, tokenContract, err :=
DeployTestERC20(getAuth(t, pk), ec, name, symbol, decimals, addr, supply)
require.NoError(t, err)
receipt := getReceipt(t, ec, tx)
t.Logf("gas cost to deploy TestERC20.sol: %d (delta %d)",
receipt.GasUsed, maxTestERC20DeployGas-int(receipt.GasUsed))
require.GreaterOrEqual(t, maxTestERC20DeployGas, int(receipt.GasUsed))
testNewSwap(t, types.EthAsset(erc20Addr), erc20Contract)
return tokenAddr, tokenContract
}
func TestSwapCreator_NewSwap_ERC20(t *testing.T) {
pkA := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)
tokenAddr, tokenContract := deployERC20Token(
t,
ec,
pkA,
"Test of the ERC20 Token",
"ERC20Token",
18,
9999,
)
testNewSwap(t, types.EthAsset(tokenAddr), tokenContract)
}
func TestSwapCreator_Claim_ERC20(t *testing.T) {
pkA := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)
addr := crypto.PubkeyToAddress(pkA.PublicKey)
erc20Addr, tx, erc20Contract, err :=
DeployTestERC20(getAuth(t, pkA), ec, "TestERC20", "TEST", 18, addr, big.NewInt(9999))
require.NoError(t, err)
receipt := getReceipt(t, ec, tx)
t.Logf("gas cost to deploy TestERC20.sol: %d (delta %d)",
receipt.GasUsed, maxTestERC20DeployGas-int(receipt.GasUsed))
require.GreaterOrEqual(t, maxTestERC20DeployGas, int(receipt.GasUsed))
tokenAddr, tokenContract := deployERC20Token(
t,
ec,
pkA,
"TestERC20",
"TEST",
18,
9999,
)
// 3 logs:
// Approval
// Transfer
// New
testClaim(t, types.EthAsset(erc20Addr), 2, big.NewInt(99), erc20Contract)
testClaim(t, types.EthAsset(tokenAddr), 2, big.NewInt(99), tokenContract)
}
func TestSwapCreator_RefundBeforeT0_ERC20(t *testing.T) {
pkA := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)
addr := crypto.PubkeyToAddress(pkA.PublicKey)
erc20Addr, tx, erc20Contract, err :=
DeployTestERC20(getAuth(t, pkA), ec, "TestERC20", "TEST", 18, addr, big.NewInt(9999))
require.NoError(t, err)
receipt := getReceipt(t, ec, tx)
t.Logf("gas cost to deploy TestERC20.sol: %d (delta %d)",
receipt.GasUsed, maxTestERC20DeployGas-int(receipt.GasUsed))
require.GreaterOrEqual(t, maxTestERC20DeployGas, int(receipt.GasUsed))
tokenAddr, tokenContract := deployERC20Token(
t,
ec,
pkA,
"TestERC20",
"TEST",
18,
9999,
)
testRefundBeforeT0(t, types.EthAsset(erc20Addr), erc20Contract, 2)
testRefundBeforeT0(t, types.EthAsset(tokenAddr), tokenContract, 2)
}
func TestSwapCreator_RefundAfterT1_ERC20(t *testing.T) {
pkA := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)
addr := crypto.PubkeyToAddress(pkA.PublicKey)
erc20Addr, tx, erc20Contract, err :=
DeployTestERC20(getAuth(t, pkA), ec, "TestERC20", "TestERC20", 18, addr, big.NewInt(9999))
require.NoError(t, err)
receipt := getReceipt(t, ec, tx)
t.Logf("gas cost to deploy TestERC20.sol: %d (delta %d)",
receipt.GasUsed, maxTestERC20DeployGas-int(receipt.GasUsed))
require.GreaterOrEqual(t, maxTestERC20DeployGas, int(receipt.GasUsed))
tokenAddr, tokenContract := deployERC20Token(
t,
ec,
pkA,
"TestERC20",
"TEST",
18,
9999,
)
testRefundAfterT1(t, types.EthAsset(erc20Addr), erc20Contract, 2)
testRefundAfterT1(t, types.EthAsset(tokenAddr), tokenContract, 2)
}

File diff suppressed because one or more lines are too long

View File

@@ -63,19 +63,8 @@ func approveERC20(t *testing.T,
require.GreaterOrEqual(t, MaxTokenApproveGas, int(receipt.GasUsed), "Token Approve")
}
func deployForwarder(t *testing.T, ec *ethclient.Client, pk *ecdsa.PrivateKey) ethcommon.Address {
forwarderAddr, err := DeployGSNForwarderWithKey(context.Background(), ec, pk)
require.NoError(t, err)
return forwarderAddr
}
func deploySwapCreatorWithForwarder(
t *testing.T,
ec *ethclient.Client,
pk *ecdsa.PrivateKey,
forwarderAddr ethcommon.Address,
) (ethcommon.Address, *SwapCreator) {
swapCreatorAddr, tx, swapCreator, err := DeploySwapCreator(getAuth(t, pk), ec, forwarderAddr)
func deploySwapCreator(t *testing.T, ec *ethclient.Client, pk *ecdsa.PrivateKey) (ethcommon.Address, *SwapCreator) {
swapCreatorAddr, tx, swapCreator, err := DeploySwapCreator(getAuth(t, pk), ec)
require.NoError(t, err)
receipt := getReceipt(t, ec, tx)
@@ -86,11 +75,6 @@ func deploySwapCreatorWithForwarder(
return swapCreatorAddr, swapCreator
}
func deploySwapCreator(t *testing.T, ec *ethclient.Client, pk *ecdsa.PrivateKey) (ethcommon.Address, *SwapCreator) {
forwarderAddr := deployForwarder(t, ec, pk)
return deploySwapCreatorWithForwarder(t, ec, pk, forwarderAddr)
}
func testNewSwap(t *testing.T, asset types.EthAsset, erc20Contract *TestERC20) {
pk := tests.GetTakerTestKey(t)
ec, _ := tests.NewEthClient(t)

View File

@@ -56,6 +56,86 @@ func StageToString(stage byte) string {
}
}
// Hash abi-encodes the RelaySwap and returns the keccak256 hash of the encoded value.
func (s *SwapCreatorRelaySwap) Hash() types.Hash {
uint256Ty, err := abi.NewType("uint256", "", nil)
if err != nil {
panic(fmt.Sprintf("failed to create uint256 type: %s", err))
}
bytes32Ty, err := abi.NewType("bytes32", "", nil)
if err != nil {
panic(fmt.Sprintf("failed to create bytes32 type: %s", err))
}
addressTy, err := abi.NewType("address", "", nil)
if err != nil {
panic(fmt.Sprintf("failed to create address type: %s", err))
}
arguments := abi.Arguments{
{
Type: addressTy,
},
{
Type: addressTy,
},
{
Type: bytes32Ty,
},
{
Type: bytes32Ty,
},
{
Type: uint256Ty,
},
{
Type: uint256Ty,
},
{
Type: addressTy,
},
{
Type: uint256Ty,
},
{
Type: uint256Ty,
},
{
Type: uint256Ty,
},
{
Type: bytes32Ty,
},
{
Type: addressTy,
},
}
args, err := arguments.Pack(
s.Swap.Owner,
s.Swap.Claimer,
s.Swap.PubKeyClaim,
s.Swap.PubKeyRefund,
s.Swap.Timeout0,
s.Swap.Timeout1,
s.Swap.Asset,
s.Swap.Value,
s.Swap.Nonce,
s.Fee,
s.RelayerHash,
s.SwapCreator,
)
if err != nil {
// As long as none of the *big.Int fields are nil, this cannot fail.
// When receiving SwapCreatorRelaySwap objects from peers in
// JSON, all *big.Int values are pre-validated to be non-nil.
panic(fmt.Sprintf("failed to pack arguments: %s", err))
}
return crypto.Keccak256Hash(args)
}
// SwapID calculates and returns the same hashed swap identifier that newSwap
// emits and that is used to track the on-chain stage of a swap.
func (sfs *SwapCreatorSwap) SwapID() types.Hash {
@@ -131,17 +211,6 @@ func GetSecretFromLog(log *ethtypes.Log, eventTopic [32]byte) (*mcrypto.PrivateS
return nil, errors.New("invalid event, must be one of Claimed or Refunded")
}
// abiSF, err := abi.JSON(strings.NewReader(SwapCreatorMetaData.ABI))
// if err != nil {
// return nil, err
// }
// data := log.Data
// res, err := abiSF.Unpack(event, data)
// if err != nil {
// return nil, err
// }
if len(log.Topics) < 3 {
return nil, errors.New("log had not enough parameters")
}
@@ -165,17 +234,6 @@ func CheckIfLogIDMatches(log ethtypes.Log, eventTopic, id [32]byte) (bool, error
return false, errors.New("invalid event, must be one of Claimed or Refunded")
}
// abi, err := abi.JSON(strings.NewReader(SwapCreatorMetaData.ABI))
// if err != nil {
// return false, err
// }
// data := log.Data
// res, err := abi.Unpack(event, data)
// if err != nil {
// return false, err
// }
if len(log.Topics) < 2 {
return false, errors.New("log had not enough parameters")
}