Skip to content

Deploy smart contract using truffle

cross-chain edited this page Mar 3, 2020 · 4 revisions

Deploy smart contract on Fusion blockchain using truffle

1. Install truffle

    npm install -g truffle

2. Creating a project

  1. Create a new directory for our Truffle project:
mkdir FsnExample
cd FsnExample
  1. Create a bare Truffle project with no smart contracts included, use
   truffle init

Once this operation is completed, we'll now have a project structure with the following items:

  • contracts/: Directory for Solidity contracts
  • migrations/: Directory for scriptable deployment files
  • test/: Directory for test files for testing your application and contracts
  • truffle-config.js: Truffle configuration file

3. Running truffle develop command

we use fusion private chain as example in this doc. we want to start a private chain locally and make truffle connect to our running priavte chain. If you want deploy contract on testnet or mainnet, please start chain and modify truffle-config.js according to the actual blockchain.

a) start Fusion private chain

reference to Deploy-private-chain for more information.

start Fusion private chain with RPC port 12001

./efsn --devnet --datadir node1 --port 12341 --rpc --rpcport 12001 --rpcapi web3,eth,net,db,personal,fsn,fsntx --rpcaddr 0.0.0.0 --rpccorsdomain "*" --ws --wsport 13001 --wsapi web3,eth,net,db,personal,fsn,fsntx --wsaddr 0.0.0.0 --wsorigins "*" --unlock 0x0122bf3930c1201a21133937ad5c83eb4ded1b08 --password passwd --miner.etherbase 0x0122bf3930c1201a21133937ad5c83eb4ded1b08 --networkid 55555  --mine --autobt --gcmode archive 2>&1 | tee -a node1.log

In our example, we should have at least three accounts:

accounts[0]:  coinbase for mining, and contract creator
accounts[1]: deposit timelock and asset from this account to  contract, test `receiveAsset`method
accounts[2]: withdraw timelock or asset from contract to this account, test `sendAsset`method

If not, we can create some new accounts:

./efsn account new --datadir node1

and save the corresponding password in file passwd

We should unlock at least two accounts to make transactions:

./efsn attach node1/efsn.ipc

> personal.unlockAccount(eth.accounts[1],null,0)

b) modify truffle-config.js

add development in networks, please ensure the host, port and network_id is same to our private chain. The fusion private chain network id is 55555, testnet is 46688, mainnet is 32659.

development: {
      host: "127.0.0.1", 
      port: 12001, 
      network_id: 55555, 
      gas: 4700000, 
      gasPrice: 1000000000,
    },

and specify solc version to "0.5.4" (higher version may not support)

  compilers: {
    solc: {
      version: "0.5.4",
    }
  }

c) truffle connect to Fusion private chain

truffle console

after enter into truffle console, we can input the following commands

web3.currentProvider.host

web3.eth.net.getId()

to verify if we have connected to our private chain successfully.

4. Create a solidity smart contract

Let's create a solidity smart contract named as FSNExample.sol in our contracts/ directory.

FSNExample.sol

pragma solidity <=0.5.10;

contract FSNContract {
    address constant precompile = address(0x9999999999999999999999999999999999999999);

    // these events will be generated by the low level impl.
    event LogFusionAssetReceived(bytes32 indexed _asset, address indexed _from, uint256 _value, uint64 _start, uint64 _end, SendAssetFlag _flag);
    event LogFusionAssetSent(bytes32 indexed _asset, address indexed _to, uint256 _value, uint64 _start, uint64 _end, SendAssetFlag _flag);

    enum SendAssetFlag {
        UseAny,                                         // 0
        UseAnyToTimeLock,                // 1
        UseTimeLock,                             // 2
        UseTimeLockToTimeLock,    // 3
        UseAsset,                                      // 4
        UseAssetToTimeLock              // 5
    }

    function _sendAsset(bytes32 asset, address to, uint256 value, uint64 start, uint64 end, SendAssetFlag flag) internal returns (bool, bytes memory) {
        bytes memory input = abi.encode(1, asset, to, value, start, end, flag);
        return precompile.call(input);
    }
}

contract FSNExample is FSNContract {
    address owner;
    modifier onlyOwner {
        require(msg.sender == owner, "only owner");
        _;
    }

    constructor() public {
        owner = msg.sender;
    }

    // If a contract want to receive Fusion Asset and TimeLock from an EOA,
    // the contract must impl the following 'receiveAsset' interface.
    function receiveAsset(bytes32 assetID, uint64 startTime, uint64 endTime, SendAssetFlag flag, uint256[] memory extraInfo) payable public returns (bool success) {
        (assetID, startTime, endTime, flag, extraInfo); // silence warning of Unused function parameter
        return true;
    }

    // impl by calling a precompiled contract '0x9999999999999999999999999999999999999999'
    // which support send out Fusion Asset and TimeLock from the calling contract.
    function sendAsset(bytes32 asset, address to, uint256 value, uint64 start, uint64 end, SendAssetFlag flag) onlyOwner public returns (bool success) {
        (success,) = _sendAsset(asset, to, value, start, end, flag);
        require(success, "call sendAsset failed");
        return true;
    }
}

5. Compiling smart contract

run truffle console to enter into truffle console, and input compile command

compile

the compiling process is like the following:

truffle(development)> compile

Compiling your contracts...
===========================
 Fetching solc version list from solc-bin. Attempt #1
 Downloading compiler. Attempt #3.
> Compiling ./contracts/FSNExample.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /home/jowen/projects/contracts/FsnExample/build/contracts
Once you compiled your smart contract class, it’s time to create a migration file. Create a migration file 2_deploy_contracts.js in /migrations folder.
> Compiled successfully using:
   - solc: 0.5.4+commit.9549d8ff.Emscripten.clang

6. Migrating smart contract

Once we compiled our smart contract class, it's time to create a migration file. Create a migration file 2_deploy_contracts.js in migrations/ directory.

2_deploy_contracts.js

var FSNExample = artifacts.require("./FSNExample.sol");

module.exports = function(deployer) {
  deployer.deploy(FSNExample);
};

Once you created the contract migration file, it is time to migrate it using truffle development cli. Let's get back to the terminal window again and type this command there.

migrate

It will start from migrating 1_initial_migration.js file and it will show various migration details like transaction hash, contract address, and total cost, etc.

truffle(development)> migrate

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Starting migrations...
======================
> Network name:    'development'
> Network id:      55555
> Block gas limit: 0x60f45e


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xa5f33ef3befe1bd2e206a48b7dfa4487f2fbad6937162c534e3cc742f121a0e4
   > Blocks: 1            Seconds: 12
   > contract address:    0xE5Ab4cb9340Ff5a25885FE5f703F69c29152Dff0
   > block number:        310
   > block timestamp:     1583132601
   > account:             0x0122BF3930c1201A21133937Ad5C83Eb4dEd1b08
   > balance:             99975775
   > gas used:            192057
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.000192057 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:         0.000192057 ETH

Then it will migrate our contract file 2_deploy_contract.js and all migration details.

2_deploy_contracts.js
=====================

   Deploying 'FSNExample'
   ----------------------
   > transaction hash:    0x46e89673e55f64dcb2e9a6512bad6a7d21410d96f2246ef44331ce72c186769d
   > Blocks: 0            Seconds: 12
   > contract address:    0x751387409a378ab28a7421F99892F6D063943E67
   > block number:        312
   > block timestamp:     1583132627
   > account:             0x0122BF3930c1201A21133937Ad5C83Eb4dEd1b08
   > balance:             99975780
   > gas used:            428304
   > gas price:           1 gwei
   > value sent:          0 ETH
   > total cost:          0.000428304 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:         0.000428304 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.000620361 ETH

Now, we can get our contract's address by the following command.

FSNExample.address

7. Reading smart contract

Once our smart contract deployed to a blockchain, now it's time to interact with it. We are going to use our truffle console to interact with it.

FSNExample.deployed().then(function(instance){ app = instance; })

Once we have created an app instance of our smart contract class, we can get complete details of FSNExample smart contract.

app

8. Writing to smart contract

Let's assign all our available accounts to a variable for preparation.

web3.eth.getAccounts().then(function(result){ accounts = result })

Now, we can check this accounts array variable. Just type accounts in our terminal window.

a) call receiveAsset method

first, let remember its interface:

function receiveAsset(bytes32 assetID, uint64 startTime, uint64 endTime, SendAssetFlag flag, uint256[] memory extraInfo) payable public returns (bool success)

then, use call method to check our calling first. Notice the way of giving value parameter. If value is not given, then nothing will be received.

app.receiveAsset.call("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",0,1614671636,0,[],{from:accounts[1],value:web3.utils.toWei("10")})

And replace call with sendTransaction when call returns true.

app.receiveAsset.sendTransaction("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",0,1614671636,0,[],{from:accounts[1],value:web3.utils.toWei("10")})

Once this command executes successfully, it will show the transaction details.

truffle(development)> app.receiveAsset.sendTransaction("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",0,1614671636,0,[],{from:accounts[1],value:web3.utils.toWei("10")})
{ tx:
   '0x765e17c22cc729fbed3b46ba28cf0b8008c75f9ca1d067c71382d936c2a8a211',
  receipt:
   { blockHash:
      '0x4355c525916c10a7a3511f974bc81056b20f3520b1a163a53f157dd2ddf337ef',
     blockNumber: 733,
     contractAddress: null,
     cumulativeGasUsed: 47097,
     from: '0x37a200388caa75edcc53a2bd329f7e9563c6acb6',
     gasUsed: 25145,
     logs: [ [Object] ],
     logsBloom:
      '0x00000000800000000000000400000000000100080000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000100000000000000000000000010000000000000000000000000200000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000',
     status: true,
     to: '0x751387409a378ab28a7421f99892f6d063943e67',
     transactionHash:
      '0x765e17c22cc729fbed3b46ba28cf0b8008c75f9ca1d067c71382d936c2a8a211',
     transactionIndex: 1,
     rawLogs: [ [Object] ] },
  logs:
   [ { address: '0x751387409a378ab28a7421F99892F6D063943E67',
       blockNumber: 733,
       transactionHash:
        '0x765e17c22cc729fbed3b46ba28cf0b8008c75f9ca1d067c71382d936c2a8a211',
       transactionIndex: 1,
       blockHash:
        '0x4355c525916c10a7a3511f974bc81056b20f3520b1a163a53f157dd2ddf337ef',
       logIndex: 1,
       removed: false,
       id: 'log_5529e6b3',
       event: 'LogFusionAssetReceived',
       args: [Result] } ] }

To check whether our message is updated in our smart contract or not, we could first check the above transaction's status and logs, then query our asset and timelock balance of our contact address and the sender address in our private blockchain.

b) call sendAsset method

first, let remember its interface:

function sendAsset(bytes32 asset, address to, uint256 value, uint64 start, uint64 end, SendAssetFlag flag) onlyOwner public returns (bool success)

then, use call method to check our calling first.

app.sendAsset.call("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",accounts[2],web3.utils.toWei("1"),0,1614671636,0,{from:accounts[0]})

And replace call with sendTransaction when call returns true.

app.sendAsset.sendTransaction("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",accounts[2],web3.utils.toWei("1"),0,1614671636,0,{from:accounts[0]})

Once this command executes successfully, it will show the transaction details.

truffle(development)> app.sendAsset.sendTransaction("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",accounts[2],web3.utils.toWei("1"),0,1614671636,0,{from:accounts[0]})
{ tx:
   '0x7bed61043c9dd5f4d4e3d34d7c6f8fcfbd76272db2a3f60d47d96d39e424077b',
  receipt:
   { blockHash:
      '0xa391b9e6a1f5fb4fc6235bbd46e18d590a2585a7cb0436fab3c82cfb7d8896c0',
     blockNumber: 750,
     contractAddress: null,
     cumulativeGasUsed: 60870,
     from: '0x0122bf3930c1201a21133937ad5c83eb4ded1b08',
     gasUsed: 38918,
     logs: [ [Object] ],
     logsBloom:
      '0x00000000800000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000010000000000000000000000000600000000000000000020000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000080200000000000',
     status: true,
     to: '0x751387409a378ab28a7421f99892f6d063943e67',
     transactionHash:
      '0x7bed61043c9dd5f4d4e3d34d7c6f8fcfbd76272db2a3f60d47d96d39e424077b',
     transactionIndex: 1,
     rawLogs: [ [Object] ] },
  logs:
   [ { address: '0x751387409a378ab28a7421F99892F6D063943E67',
       blockNumber: 750,
       transactionHash:
        '0x7bed61043c9dd5f4d4e3d34d7c6f8fcfbd76272db2a3f60d47d96d39e424077b',
       transactionIndex: 1,
       blockHash:
        '0xa391b9e6a1f5fb4fc6235bbd46e18d590a2585a7cb0436fab3c82cfb7d8896c0',
       logIndex: 1,
       removed: false,
       id: 'log_b88c6352',
       event: 'LogFusionAssetSent',
       args: [Result] } ] }

To check whether our message is updated in our smart contract or not, we could first check the above transaction's status and logs, then query our asset and timelock balance of our contact address and the receiver address in our private blockchain.