Deposit Assets

Early on in the lifecycle of a state channel -- i.e. after exchanging some setup states, but before executing any application logic -- participants will want to "fund it". They will stake assets on the channel so that the state updates are meaningful. The simplest way to do this is with an on chain deposit; a more advanced possibility is fund a new channel from an existing funded channel.

To start, let's explore the on chain deposit case. We will need to understand how channels are uniquely identified.

Compute a channel id

The id of a channel is the keccak256 hash of the abi encoded chainId, participants and channelNonce.

By choosing a new channelNonce each time the same participants execute a state channel supported by the same chain, they can avoid replay attacks. The getChannelId helper exported by @statechannels/nitro-protocol accepts an object of type Channel (which contains all the necessary properties) and computes the channel id for you.

// In lesson4.test.ts
// Import Ethereum utilities
import {Wallet} from 'ethers';
// Import statechannels utilities
import {Channel, getChannelId} from '@statechannels/nitro-protocol';
const participants = [];
for (let i = 0; i < 3; i++) {
// As many participants as you like
participants[i] = Wallet.createRandom().address;
}
const chainId = '0x1234'; // E.g. '0x1' for mainnnet. Using a mock here
const channelNonce = 0;
const channel: Channel = {chainId, channelNonce, participants};
const channelId = getChannelId(channel);

deposit into channel

The deposit method allows ETH or ERC20 tokens to be escrowed against a channel. We have the following call signature:

function deposit(address asset, bytes32 destination, uint256 expectedHeld, uint256 amount) public payable

There are a few rules to obey when calling deposit. Firstly, destination must NOT be an external destination. Secondly, the on-chain holdings for destination must be greater than or equal to expectedHeld. Thirdly, the holdings for destination must be less than the sum of the amount expected to be held and the amount declared in the deposit.

The first rule prevents funds being escrowed against something other than a channelId: funds may only be unlocked from channels, so you shouldn't deposit into anything else. The second rule prevents loss of funds: since holdings are paid out in preferential order, depositing before a counterparty has deposited implies that they can withdraw your funds. The check is performed in the same transaction as the deposit, making this safe even in the event of a chain re-org that reverts a previous participant's deposit. The third rule prevents the deposit of uneccessary funds: if my aim was to increase the holdings to a certain level, but they are already at or above that level, then I want my deposit to transaction revert.

If we are depositing ETH, we must remember to send the right amount of ETH with the transaction, and to set the asset parameter to the zero address.

import {ethers} from 'ethers';
import {randomChannelId, randomChannelId} from '@statechannels/nitro-protocol';
// In lesson5.test.ts
/*
Get an appropriate representation of 1 wei, and
use randomChannelId() as a dummy channelId.
WARNING: don't do this in the wild: you won't be able to recover these funds.
*/
const amount = ethers.utils.parseUnits('1', 'wei');
const destination = randomChannelId();
/*
Attempt to deposit 1 wei against the channel id we created.
Inspect the error message in the console for a hint about the bug on the next line
*/
const expectedHeld = 0;
const tx0 = NitroAdjudicator.deposit(constants.AddressZero, destination, expectedHeld, amount, {
value: amount
});

Otherwise, if we are depositing ERC20 tokens, we must remember to approve the NitroAdjudicator for enough tokens before making the deposit.

Destinations

A Destination is a bytes32 and either:

  1. A ChannelId (see the section on channelId), or
  2. An ExternalDestination, which is an ethereum address left-padded with zeros.
tip

In JavaScript, the ExternalDestination corresponding to address may be computed as

'0x' + address.padStart(64, '0')

or

ethers.utils.hexZeroPad(address, 32);
Last updated on by George C. Knee