Skip to content
This repository was archived by the owner on Dec 21, 2021. It is now read-only.
Merged
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
15 changes: 15 additions & 0 deletions src/dataunion/DataUnion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,21 @@ export class DataUnion {
return tx.wait()
}

/**
* Transfer amount to specific member in DataunionSidechain
* @param memberAddress - the other member who gets their tokens out of the Data Union
* @param amountTokenWei - the amount that want to add to the member
* @returns receipt once transfer transaction is confirmed
*/
async transferToMemberInContract(
memberAddress: EthereumAddress,
amountTokenWei: BigNumber|number|string
): Promise<TransactionResponse> {
const address = getAddress(memberAddress) // throws if bad address
const duSidechain = await this.getContracts().getSidechainContract(this.contractAddress)
return duSidechain.transferToMemberInContract(address, amountTokenWei)
}

/**
* Create a new DataUnionMainnet contract to mainnet with DataUnionFactoryMainnet
* This triggers DataUnionSidechain contract creation in sidechain, over the bridge (AMB)
Expand Down
9 changes: 9 additions & 0 deletions src/dataunion/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ export const dataUnionSidechainABI = [{
],
anonymous: false,
type: 'event'
}, {
name: 'transferToMemberInContract',
inputs: [
{ name: 'recipient', type: 'address', internalType: 'address' },
{ name: 'amount', type: 'uint256', internalType: 'uint256' }
],
outputs: [],
stateMutability: 'nonpayable',
type: 'function'
}]

// Only the part of ABI that is needed by deployment (and address resolution)
Expand Down
145 changes: 145 additions & 0 deletions test/integration/dataunion/transfer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { BigNumber, Contract, providers, Wallet } from 'ethers'
import { parseEther } from 'ethers/lib/utils'
import debug from 'debug'
import * as Token from '../../../contracts/TestToken.json'
import config from '../config'
import { getEndpointUrl, until } from '../../../src/utils'
import { MemberStatus } from '../../../src/dataunion/DataUnion'
import { StreamrClient } from '../../../src/StreamrClient'
import { EthereumAddress } from '../../../src/types'
import authFetch from '../../../src/rest/authFetch'

const log = debug('StreamrClient::DataUnion::integration-test-transfer')

const providerSidechain = new providers.JsonRpcProvider(config.clientOptions.sidechain)
const providerMainnet = new providers.JsonRpcProvider(config.clientOptions.mainnet)

const tokenAdminWallet = new Wallet(config.tokenAdminPrivateKey, providerMainnet)
const tokenMainnet = new Contract(config.clientOptions.tokenAddress, Token.abi, tokenAdminWallet)

const adminWalletSidechain = new Wallet(config.clientOptions.auth.privateKey, providerSidechain)
const tokenSidechain = new Contract(config.clientOptions.tokenSidechainAddress, Token.abi, adminWalletSidechain)

const sendTokensToSidechain = async (receiverAddress: EthereumAddress, amount: BigNumber) => {
const relayTokensAbi = [
{
inputs: [
{
internalType: 'address',
name: 'token',
type: 'address'
},
{
internalType: 'address',
name: '_receiver',
type: 'address'
},
{
internalType: 'uint256',
name: '_value',
type: 'uint256'
},
{
internalType: 'bytes',
name: '_data',
type: 'bytes'
}
],
name: 'relayTokensAndCall',
outputs: [],
stateMutability: 'nonpayable',
type: 'function'
}
]
const tokenMediator = new Contract(config.tokenMediator, relayTokensAbi, tokenAdminWallet)
const tx1 = await tokenMainnet.approve(tokenMediator.address, amount)
await tx1.wait()
log('Approved')
const tx2 = await tokenMediator.relayTokensAndCall(tokenMainnet.address, receiverAddress, amount, '0x1234') // dummy 0x1234
await tx2.wait()
log('Relayed tokens')
await until(async () => !(await tokenSidechain.balanceOf(receiverAddress)).eq('0'), 300000, 3000)
log('Sidechain balance changed')
}

describe('DataUnion transfer within contract', () => {
let adminClient: StreamrClient

beforeAll(async () => {
log(`Connecting to Ethereum networks, config = ${JSON.stringify(config)}`)
const network = await providerMainnet.getNetwork()
log('Connected to "mainnet" network: ', JSON.stringify(network))
const network2 = await providerSidechain.getNetwork()
log('Connected to sidechain network: ', JSON.stringify(network2))
log(`Minting 100 tokens to ${tokenAdminWallet.address}`)
const tx1 = await tokenMainnet.mint(tokenAdminWallet.address, parseEther('100'))
await tx1.wait()

await sendTokensToSidechain(adminWalletSidechain.address, parseEther('10'))

adminClient = new StreamrClient(config.clientOptions as any)
}, 150000)

afterAll(() => {
providerMainnet.removeAllListeners()
providerSidechain.removeAllListeners()
})

it('transfer token to member', async () => {
const dataUnion = await adminClient.deployDataUnion()
const secret = await dataUnion.createSecret('test secret')
// eslint-disable-next-line no-underscore-dangle
const contract = await dataUnion._getContract()
log(`DU owner: ${await dataUnion.getAdminAddress()}`)
log(`Sending tx from ${await adminClient.getAddress()}`)

// product is needed for join requests to analyze the DU version
const createProductUrl = getEndpointUrl(config.clientOptions.restUrl, 'products')
await authFetch(createProductUrl, adminClient.session, {
method: 'POST',
body: JSON.stringify({
beneficiaryAddress: dataUnion.getAddress(),
type: 'DATAUNION',
dataUnionVersion: 2
})
})

const memberWallet = new Wallet(`0x100000000000000000000000000000000000000012300000001${Date.now()}`, providerSidechain)
const memberClient = new StreamrClient({
...config.clientOptions,
auth: {
privateKey: memberWallet.privateKey
}
} as any)
const res = await memberClient.getDataUnion(dataUnion.getAddress()).join(secret)
log(`Member joined data union: ${JSON.stringify(res)}`)
log(`DU member count: ${await contract.sidechain.activeMemberCount()}`)

const stats = await memberClient.getDataUnion(dataUnion.getAddress()).getMemberStats(memberWallet.address)
log(`Stats: ${JSON.stringify(stats)}`)

const approve = await tokenSidechain.approve(dataUnion.getSidechainAddress(), parseEther('1'))
await approve.wait()
log(`Approve DU ${dataUnion.getSidechainAddress()} to access 1 token from ${adminWalletSidechain.address}`)

const tx = await dataUnion.transferToMemberInContract(memberWallet.address, parseEther('1'))
await tx.wait()
log(`Transfer 1 token with transferWithinContract to ${memberWallet.address}`)

const newStats = await memberClient.getDataUnion(dataUnion.getAddress()).getMemberStats(memberWallet.address)
log(`Stats: ${JSON.stringify(newStats)}`)

expect(stats).toMatchObject({
status: MemberStatus.ACTIVE,
earningsBeforeLastJoin: BigNumber.from(0),
totalEarnings: BigNumber.from(0),
withdrawableEarnings: BigNumber.from(0)
})
expect(newStats).toMatchObject({
status: MemberStatus.ACTIVE,
earningsBeforeLastJoin: parseEther('1'),
totalEarnings: parseEther('1'),
withdrawableEarnings: parseEther('1')
})
}, 150000)
})