11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Symbol[XYM] <-> ETH, Polygon[MATIC] などのスワップ備忘録

Last updated at Posted at 2022-12-10

準備

node, npm install済み前提
※自分の環境
node v16.1.1
npm 8.0.0
Mac Os 12.2.1
Apple M1 Pro

truffleインストール

npm install -g truffle

Ganacheインストール

HTLC作成

これを使う、ちょっと加工するだけなのでSolidity知識は不要。ただし、最低限の知識はあったほうがいいかも

クリプトゾンビ一周で良いと思われ

適当な作業フォルダを作成しておく

mkdir swap-test
cd swap-test

clone

git clone https://github.com/chatch/hashed-timelock-contract-ethereum.git
cd hashed-timelock-contract-ethereum

npm install

npm install

Contractの書き換え

ERC20やERC721も今後使うかもしれないのでひとまず全部変えておく。
ただし内容は同じでこれだけ

  • contracts/HashedTimelock.sol
  • contracts/HashedTimelockERC20.sol
  • contracts/HashedTimelockERC721.sol
modifier hashlockMatches(bytes32 _contractId, bytes32 _x) {
- require(
-             contracts[_contractId].hashlock == sha256(abi.encodePacked(_x)),
-             "hashlock hash does not match"
-         );

+ bytes32 pre = sha256(abi.encodePacked(_x));
+         require(
+             contracts[_contractId].hashlock == sha256(abi.encodePacked(pre)),
+             "hashlock hash does not match"
+         );
        _;
    }

以下は別に触らなくてもいいけどTest(npm run test)する時にエラー出さないために変えておいたほうが無難

  • test/helper/utils.js
const newSecretHashPair = () => {
  const secret = random32()
-  const hash = sha256(secret)
+  const hash = sha256(sha256(secret))
  return {
    secret: bufToStr(secret),
    hash: bufToStr(hash),
  }
}

変えるのは、これだけw

ネットワーク立ち上げ

Ganacheを起動して、NewWorkSpaceをクリック

  • ワークスペースネームは適当
  • [ADD PROJECT]をクリックして先程Cloneしたルートフォルダにあるtruffle-config.jsを選択する。
  • [SERVER]を選択し[NETWORK ID]を4447に変更
  • 右上の[SAVE WORKSPACE]をクリックする。

ブロックチェーンへの書き込み

truffle migrate

HashedTimelockだけ抜粋、ここのcontract addressをあとで使う

Deploying 'HashedTimelock'
   --------------------------
   > transaction hash:    0x6d1aed16ac35e464b6fe40237f4fd8279a7b55a481e946abdbc5401929490cce
   > Blocks: 0            Seconds: 0
   > contract address:    0xA0fA8F128FC5D438C5CbD1b4Ec0839ce797d7337
   > block number:        3
   > block timestamp:     1670671838
   > account:             0xd9F8E9bC584083eE86c9E04056ffdF7f448012f9
   > balance:             99.97453284
   > gas used:            1005758 (0xf58be)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.02011516 ETH

一応テスト

さきほど、ちゃんと書き換えていれば全部通るはず

npm run test

これでSolidiy側は終わりです

実行ファイルの作成

cd ../
mkdir test
cd test

we3jsインストール

npm init
npm install web3

abi のコピー

mkdir abi
cp ../hashed-timelock-contract-ethereum/build/contracts/HashedTimelock.json abi/HashedTimelock.json

js書く

touch htlc.js
htlc.js
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"));
const abi = require('./abi/HashedTimelock.json').abi;
const crypto = require('crypto')

const bufToStr = b => '0x' + b.toString('hex')
const sha256 = x =>
  crypto
    .createHash('sha256')
    .update(x)
    .digest()
const random32 = () => crypto.randomBytes(32)


// randomSecretからHASH256()でハッシュ作成
const newSecretHashPair = () => {
  const secret = random32()
  const hash = sha256(sha256(secret))
  return {
    // Symbolで言うところのProof
    secret: bufToStr(secret),
    // Symbolで言うところのSecret、名前ややこしいから変えてもいい
    hash: bufToStr(hash),
  }
}
const nowSeconds = () => Math.floor(Date.now() / 1000)
const hourSeconds = 3600
const timeLock1Hour = nowSeconds() + hourSeconds

// migrateした時のコントラクトアドレスを使う
const contractAddress = "CONTRACT_ADDRESS";
const htlc = new web3.eth.Contract(abi, contractAddress);

// ロックコントラクトの情報取得(Ganacheからでも分かる)
const contractInfo = (contractId) => {
    return htlc.methods.getContract(contractId).call()
}

// 新しいロックコントラクトの作成(Symbolで言うシークレットロック)
const newContract = (sender, receiver, amount) => {
    const hashPair = newSecretHashPair()
    const oneFinney = web3.utils.toWei(web3.utils.toBN(amount), 'finney')
    htlc.methods.newContract(
        receiver,
        hashPair.hash,
        timeLock1Hour,
    )
    .send(
        {
            from: sender,
            value: oneFinney,
            gas: '1000000'
        }
    )
    .on("receipt", function(receipt) {
        console.log(receipt.events.LogHTLCNew.returnValues);
    })
    .on("error", function(error) {
        console.error(error);
    });
    console.log(hashPair);
}

// Secretを使って引き出し(Symbolで言うシークレットプルーフ)
const withDraw = (contractId, secret, receiver)=> {
    htlc.methods.withdraw(
        contractId,
        secret,
    )
    .send(
        {
            from: receiver,
            gas: '1000000'
        }
    )
    .on("receipt", function(receipt) {
        console.log(receipt.events.LogHTLCWithdraw);
    })
    .on("error", function(error) {
        console.error(error);
    });
}
(async()=>{
    const accounts = await web3.eth.getAccounts();
    console.log('accounts: ', accounts);
    newContract(accounts[0], accounts[1], 1);
    //console.log(await contractInfo("CONTRACT_ID"));
    //withDraw("CONTRACT_ID", "SECRET", accounts[1]);
})();

ここまでの使い方

コントラクトアドレスを書き込む

const contractAddress = "0xA0fA8F128FC5D438C5CbD1b4Ec0839ce797d7337";
  • 新しいコントラクト(Symbolで言うところのシークレットロック)の作成
node htlc.js
accounts:  [
  '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  '0x1569cE8620F3bBFe8d647952b972F2fA76787417',
  '0xCFd150D41842804b04fa6F81e8BE6Bd11c97d24b',
  '0x0F56108C77bA0Bc3193452892D379C89dfA75613',
  '0xF54242dAF4aa5b222087c9711477c40c499581Fe',
  '0x3BAfa87546444319298fACa5Af0Ef253740A81f5',
  '0x7Cea903cfc65Fa662C909f2D60e3fBdc86198750',
  '0x9025ce74a70C6B9ac26F89065c38AfF562eB8D1B',
  '0x013342aB590070A56b020d4bf2CdCaFb59B6E6b4'
]
{
  secret: '0x93c45674bbc165f3b755b8931f3fb5b5df9655507409cdb35795ffe4e2aae52f',
  hash: '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a'
}
Result {
  '0': '0x18d80f36686d946f6357fbba0b77696048eeb12aae341058f76a6486c54b2873',
  '1': '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  '2': '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  '3': '1000000000000000',
  '4': '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  '5': '1670678842',
  contractId: '0x18d80f36686d946f6357fbba0b77696048eeb12aae341058f76a6486c54b2873',
  sender: '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  receiver: '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  amount: '1000000000000000',
  hashlock: '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  timelock: '1670678842'
}

これでロック完了。
この時のSecretの値がのちほど必要。Hashは公開情報

せっかくなので情報取得

(async()=>{
    const accounts = await web3.eth.getAccounts();
    console.log('accounts: ', accounts);
-    newContract(accounts[0], accounts[1], 1);
-    //console.log(await contractInfo("CONTRACT_ID"));
+    console.log(await contractInfo("0x18d80f36686d946f6357fbba0b77696048eeb12aae341058f76a6486c54b2873"));
    //withDraw("CONTRACT_ID", "SECRET", accounts[1]);
})();

実行

node htlc.js
Result {
  '0': '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  '1': '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  '2': '1000000000000000',
  '3': '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  '4': '1670678842',
  '5': false,
  '6': false,
  '7': '0x0000000000000000000000000000000000000000000000000000000000000000',
  sender: '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  receiver: '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  amount: '1000000000000000',
  hashlock: '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  timelock: '1670678842',
  withdrawn: false,
  refunded: false,
  preimage: '0x0000000000000000000000000000000000000000000000000000000000000000'
}

hashは公開されるけどsecretは非公開情報(preimage: '0x0000000000000000000000000000000000000000000000000000000000000000')。なお、このへんはGaracheのトランザクション情報を掘ると見れる。

WithDraw(Symbolで言うところのシークレットプルーフ)

第一引数をコントラクトID、第二引数にSecretを記入する

(async()=>{
    const accounts = await web3.eth.getAccounts();
    console.log('accounts: ', accounts);
    //newContract(accounts[0], accounts[1], 1);
-    //console.log(await contractInfo("0x18d80f36686d946f6357fbba0b77696048eeb12aae341058f76a6486c54b2873"));
-    //withDraw("CONTRACT_ID", "SECRET", accounts[1]);
+    withDraw("0x18d80f36686d946f6357fbba0b77696048eeb12aae341058f76a6486c54b2873", "0x93c45674bbc165f3b755b8931f3fb5b5df9655507409cdb35795ffe4e2aae52f", accounts[1]);
})();

実行

node htlc.js

エラーが無ければOK。
最後にもう一度、contractInfo()を実行してみると

Result {
  '0': '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  '1': '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  '2': '1000000000000000',
  '3': '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  '4': '1670678842',
  '5': true,
  '6': false,
  '7': '0x93c45674bbc165f3b755b8931f3fb5b5df9655507409cdb35795ffe4e2aae52f',
  sender: '0x9548d0aF212baA56B66c652A548F00de3C7E3927',
  receiver: '0xB74f347B42a4A998f154608A7F74dD526E4edc44',
  amount: '1000000000000000',
  hashlock: '0x5eb0d90785a2dd629f0b6a8dafda0d8fb0eaf76983d63591df7c6d644c65b13a',
  timelock: '1670678842',
  withdrawn: true,
  refunded: false,
  preimage: '0x93c45674bbc165f3b755b8931f3fb5b5df9655507409cdb35795ffe4e2aae52f'
}

preimage: '0x93c45674bbc165f3b755b8931f3fb5b5df9655507409cdb35795ffe4e2aae52f'となりSecretが公開情報となる

テストネットに展開する

以下参考

結論、簡単だった。

イーサリアムのテストネット(Sepolia)へのスマートコントラクトの展開

記事内ではRinkebyを使っているが、おそらく終了したテストネットぽいのでSepoliaを使う。

MetaMaskの設定

  • MetaMask 設定 -> 高度な設定 -> テストネットワークを表示 -> ON
  • Sepoliaテストネットワークを選択
  • 受け渡しテスト用にアカウントを追加しておく

Faucetより両アカウントにETHを取得

ロックトランザクションはアカウント1から、引き出し(いわゆるプルーフ)はアカウント2から行うので両アカウントに手数料が必要。
なお、アカウント1でmigrateをするのでその手数料も必要。一度のFaucetからの取得で足りる。

デプロイ

infuraアカウント作成

  • Project作成
  • EndPoints -> Ethereum -> Sepolia を選択しエンドポイントURLをコピー

truffle-config.js 編集

hashed-timelock-contract-ethereum@truffle/hdwallet-providerを追加する

npm install @truffle/hdwallet-provider
truffle-config.js
const HDWalletProvider = require('@truffle/hdwallet-provider')
const privateKeys = ['PRIVATE_KEY']
module.exports = {
  networks: {
    development: {
      provider: () => new
      HDWalletProvider(privateKeys, 'SEPOLIA_ENDPOINT'),
      network_id: 11155111,
    },
  }
}

truffle-config.jsを編集。
アカウント1の秘密鍵(MetaMaskから取得)と先程コピーしたエンドポイントを編集する
なおSepoliaのnetwork_id11155111

Migrate

truffle migrate -network development --reset

以上。
3つのコントラクトがデプロイされるので、コントラクトアドレスを控えておく。

test

ローカルでテストしたのと同じことをやる。
変更点は

htlc.js
const web3 = new Web3(new Web3.providers.HttpProvider("ENDPOINT"));
-----
const contractAddress = "CONTRACT_ADDRESS";

ここだけ。

スマートコントラクトをPolygonのテストネット(ムンバイ)に展開する

MetaMask

Polygonはもっと簡単。

  • MetaMask -> ネットワーク -> ネットワーク追加 -> PolygonテストネットMumbaiを追加
Network name: PolygonTestnet
RPC URL: https://rpc-mumbai.maticvigil.com
Chain ID: 80001
Currency symbol: MATIC
Block Explorer URL: https://mumbai.polygonscan.com/
  • faucetから両アカウントにMaticを取得

truffle-config.js 編集

truffle-config.js
const HDWalletProvider = require('@truffle/hdwallet-provider')
const privateKeys = ['PRIVATE_KEY']
module.exports = {
  networks: {
    development: {
      provider: () => new
      HDWalletProvider(privateKeys, 'SEPOLIA_ENDPOINT'),
      network_id: 11155111,
    },
    polygonTestnet: {
      provider: () => new
      HDWalletProvider(privateKeys, `https://rpc-mumbai.maticvigil.com`),
      network_id: 80001,
    }
  }
}

Migrate

truffle migrate -network polygonTestnet --reset

以上。Ethテストネットと同じ用にPolygonテストネットでも確認。

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?