Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

implement a swap with ninja nitro #5

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ jobs:
- run: yarn install
- run: yarn go-vector
- run: yarn go-nitro
- run: yarn go-ninja
- run: yarn dispute-nitro
- run: yarn dispute-vector
15 changes: 8 additions & 7 deletions common/two-chain-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function spinUpChains() {
console.log(`ganache listening on port ${left.port}...`);
});
const leftChain = new ethers.providers.JsonRpcProvider(
`http://localhost:${left.port}`,
`http://localhost:${left.port}`
);
const right = {
gasPrice: ethers.constants.One.toHexString(),
Expand All @@ -56,7 +56,7 @@ export function spinUpChains() {
console.log(`ganache listening on port ${right.port}...`);
});
const rightChain = new ethers.providers.JsonRpcProvider(
`http://localhost:${right.port}`,
`http://localhost:${right.port}`
);

async function tearDownChains() {
Expand Down Expand Up @@ -88,19 +88,19 @@ export class Actor {
this.log(
`I have ${(
await this.getLeftBalance()
).toString()} tokens on the left chain`,
).toString()} tokens on the left chain`
);
this.log(
`I have ${(
await this.getRightBalance()
).toString()} tokens on the right chain`,
).toString()} tokens on the right chain`
);
}

constructor(
public signingWallet: ethers.Wallet,
public leftToken: Contract, // TODO allow this to be undefined and fall back on an ETH swap
public rightToken: Contract,
public rightToken: Contract
) {}
}

Expand Down Expand Up @@ -137,7 +137,7 @@ export async function logTotalGasSpentByAll(...actors: Actor[]) {
// Copied from https://github.com/connext/vector/blob/c8a8deed53cfa7caa58a6466d82fe5d22c208622/modules/contracts/src.ts/utils.ts#L29
export async function advanceBlocktime(
provider: ethers.providers.JsonRpcProvider,
seconds: number,
seconds: number
): Promise<void> {
const { timestamp: currTime } = await provider.getBlock("latest");
await provider.send("evm_increaseTime", [seconds]);
Expand All @@ -160,11 +160,12 @@ export async function parseTransaction(chain, tx, action: string) {
const refunds = costPerOpcode.filter((item) => item.gas < 0);

console.log(_.concat(bigSpenders, ["..."], refunds));
return gasUsed;
}

async function aggregatedCostPerOpcode(
chain: ethers.providers.JsonRpcProvider,
txHash: string,
txHash: string
) {
const trace = await chain.send("debug_traceTransaction", [txHash, {}]);

Expand Down
148 changes: 148 additions & 0 deletions ninja-atomic-swap/happy-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { ethers } from "ethers";
import { State, signState } from "@statechannels/nitro-protocol";
import { LEFT_CHAIN_ID, RIGHT_CHAIN_ID } from "../constants";
import {
Executor,
logBalances,
logTotalGasSpentByAll,
Responder,
spinUpChains,
} from "../common/two-chain-setup";
import { deployContractsToChain } from "./helpers";
import {
createHashLockChannel,
fundChannel,
preImage,
correctPreImage,
decodeHashLockedSwapData,
defundChannel,
encodeHashLockedSwapData,
swap,
} from "./helpers";

const { leftChain, rightChain, tearDownChains } = spinUpChains();

// Spin up two instances of ganache.
// Deploy NitroAdjudicator, ERC20AssetHolder, HashLock to both instances
// Run an atomic swap between the chains (Happy Case, Direct Funding)
// Record time taken and gas consumed
// Explore unhappy cases
// Explore off-chain funding use case

// *****

(async function () {
// SETUP CONTRACTS ON BOTH CHAINS
// Deploy the contracts to chain, and then reconnect them to their respective signers
// for the rest of the interactions
const [
leftAdjudicatorFactory,
leftMasterCopy,
leftHashLock,
leftToken,
] = await deployContractsToChain(leftChain);
const [
rightAdjudicatorFactory,
rightMasterCopy,
rightHashLock,
rightToken,
] = await deployContractsToChain(rightChain);

const executor = new Executor(leftToken, rightToken);
const responder = new Responder(leftToken, rightToken);

await logBalances(executor, responder);

const _PreFund0 = createHashLockChannel(
LEFT_CHAIN_ID,
60,
leftHashLock.address,
leftToken.address,
executor,
responder,
ethers.utils.sha256(preImage)
);

// exchanges setup states and funds on left chain
const longChannel = await fundChannel(
leftChain,
leftAdjudicatorFactory,
leftToken,
_PreFund0,
executor,
responder
);

// given the longChannel is now funded and running
// the responder needs to incentivize the executor to do the swap
const _preFund0 = createHashLockChannel(
RIGHT_CHAIN_ID,
30,
rightHashLock.address,
rightToken.address,
responder,
executor,
decodeHashLockedSwapData(_PreFund0.appData).h
);

const shortChannel = await fundChannel(
rightChain,
rightAdjudicatorFactory,
rightToken,
_preFund0,
responder,
executor
);

// await logBalances(executor, responder); // uncomment this to check deposit was legit

// executor unlocks payment that benefits him
const _unlock4: State = {
..._preFund0,
turnNum: 4,
appData: encodeHashLockedSwapData(correctPreImage),
outcome: swap(_preFund0.outcome),
};
const unlock4 = signState(_unlock4, executor.signingWallet.privateKey);

// responder decodes the preimage and unlocks the payment that benefits her
const decodedPreImage = decodeHashLockedSwapData(unlock4.state.appData)
.preImage;
const decodedHash = decodeHashLockedSwapData(unlock4.state.appData).h;
const _Unlock4: State = {
..._PreFund0,
turnNum: 4,
appData: encodeHashLockedSwapData({
h: decodedHash,
preImage: decodedPreImage,
}),
outcome: swap(_PreFund0.outcome),
};
const Unlock4 = signState(_Unlock4, responder.signingWallet.privateKey);

// both channels are collaboratively defunded
await Promise.all([
defundChannel(
_preFund0,
_unlock4,
responder,
executor,
rightHashLock,
rightAdjudicatorFactory
),
defundChannel(
_PreFund0,
_Unlock4,
executor,
responder,
leftHashLock,
leftAdjudicatorFactory
),
]);

await logBalances(executor, responder);
logTotalGasSpentByAll(executor, responder);

// teardown blockchains
await tearDownChains();
})();
Loading