Skip to content

Commit

Permalink
Client fix for evaluating ETH balance across networks (#1001)
Browse files Browse the repository at this point in the history
This PR involves an API change to allow harmony / other users to pass in
which supported xchain ids are ether-based chains.

^^ requires a small harmony refactor.

Changes:
- Updated API to take an `XChainConfig` that is extensible with future
information and expresses which supported networks use ether as a
network fee currency
- fix typescript implementation to check across all supported eth-based
networks
- update unit and e2e tests to be cross-network
  • Loading branch information
clemire authored Sep 11, 2024
1 parent 511a98d commit 425f860
Show file tree
Hide file tree
Showing 9 changed files with 489 additions and 302 deletions.
102 changes: 71 additions & 31 deletions packages/sdk/src/channelsWithEntitlements.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
expectUserCanJoin,
everyoneMembershipStruct,
linkWallets,
getXchainSupportedRpcUrlsForTesting,
getXchainConfigForTesting,
erc20CheckOp,
customCheckOp,
nativeCoinBalanceCheckOp,
setupChannelWithCustomRole,
expectUserCanJoinChannel,
expectUserCannotJoinChannel,
ethBalanceCheckOp,
} from './util.test'
import { MembershipOp } from '@river-build/proto'
import { dlog } from '@river-build/dlog'
Expand All @@ -43,9 +43,10 @@ import {
import { make_MemberPayload_KeySolicitation } from './types'

const log = dlog('csb:test:channelsWithEntitlements')
const twoEth = BigInt(2e18)
const oneEth = BigInt(1e18)
const oneHalfEth = BigInt(5e17)
const oneEth = oneHalfEth * BigInt(2)
const twoEth = oneEth * BigInt(2)
const gtTwoEth = twoEth + BigInt(1)

describe('channelsWithEntitlements', () => {
test('User who satisfies only one role ruledata requirement can join channel', async () => {
Expand Down Expand Up @@ -374,7 +375,7 @@ describe('channelsWithEntitlements', () => {
channelId!,
alice.userId,
Permission.Read,
getXchainSupportedRpcUrlsForTesting(),
getXchainConfigForTesting(),
),
).resolves.toBeFalsy()

Expand Down Expand Up @@ -1113,15 +1114,13 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gate pass', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(oneEth))
test('eth balance gate pass', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(oneEth))

const { alice, bob, alicesWallet, aliceSpaceDapp, spaceId, channelId } =
await setupChannelWithCustomRole([], ruleData)

await TestEthBalance.setBalance(alicesWallet.address as Address, oneEth)
const balance = await TestEthBalance.getBalance(alicesWallet.address as Address)
expect(balance).toEqual(oneEth)
await Promise.all([TestEthBalance.setBaseBalance(alicesWallet.address as Address, oneEth)])

log('expect that alice can join the channel')
await expectUserCanJoinChannel(alice, aliceSpaceDapp, spaceId, channelId!)
Expand All @@ -1133,13 +1132,36 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gate fail', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(oneEth))
test('eth balance gate pass - across networks', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(oneEth))

const { alice, bob, alicesWallet, aliceSpaceDapp, spaceId, channelId } =
await setupChannelWithCustomRole([], ruleData)

await TestEthBalance.setBalance(alicesWallet.address as Address, 0n)
await Promise.all([
TestEthBalance.setBaseBalance(alicesWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(alicesWallet.address as Address, oneHalfEth),
])

log('expect that alice can join the channel')
await expectUserCanJoinChannel(alice, aliceSpaceDapp, spaceId, channelId!)

// kill the clients
const doneStart = Date.now()
await bob.stopSync()
await alice.stopSync()
log('Done', Date.now() - doneStart)
})

test('eth balance gate fail', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(oneEth))

const { alice, bob, alicesWallet, aliceSpaceDapp, spaceId, channelId } =
await setupChannelWithCustomRole([], ruleData)

// alice's base wallet may need to be explicitly set to zero to compensate for wallet funding in
// initialization methods.
await Promise.all([TestEthBalance.setBaseBalance(alicesWallet.address as Address, 0n)])

log('expect that alice cannot join the channel (has no ETH)')
await expectUserCannotJoinChannel(alice, aliceSpaceDapp, spaceId, channelId!)
Expand All @@ -1151,8 +1173,8 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gate join pass - join as root, linked wallet entitled', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(oneEth))
test('eth balance gate join pass - join as root, linked wallet entitled', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(oneEth))
const {
alice,
bob,
Expand All @@ -1168,14 +1190,19 @@ describe('channelsWithEntitlements', () => {
// Link carol's wallet to alice's as root
await linkWallets(aliceSpaceDapp, aliceProvider.wallet, carolProvider.wallet)

// Set wallet balances to 0
await TestEthBalance.setBalance(carolsWallet.address as Address, 0n)
await TestEthBalance.setBalance(alicesWallet.address as Address, 0n)
// Explicitly set wallet balances to 0
await Promise.all([
TestEthBalance.setBaseBalance(carolsWallet.address as Address, 0n),
TestEthBalance.setBaseBalance(alicesWallet.address as Address, 0n),
])

// Validate alice cannot join the channel
await expectUserCannotJoinChannel(alice, aliceSpaceDapp, spaceId, channelId!)

await TestEthBalance.setBalance(carolsWallet.address as Address, oneEth)
await Promise.all([
TestEthBalance.setBaseBalance(carolsWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(carolsWallet.address as Address, oneHalfEth),
])

// Wait 2 seconds for the negative auth cache to expire
await new Promise((f) => setTimeout(f, 2000))
Expand All @@ -1190,8 +1217,8 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gated join pass - join as linked wallet, assets in root wallet', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(oneEth))
test('eth balance gated join pass - join as linked wallet, assets in root wallet', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(twoEth))
const {
alice,
bob,
Expand All @@ -1209,8 +1236,13 @@ describe('channelsWithEntitlements', () => {
await linkWallets(carolSpaceDapp, carolProvider.wallet, aliceProvider.wallet)

log("Setting carol and alice's wallet balances to 1ETH and 0, respectively")
await TestEthBalance.setBalance(carolsWallet.address as Address, oneEth)
await TestEthBalance.setBalance(alicesWallet.address as Address, 0n)
// Carol's cumulative balance across wallets: 2ETH
// Alice's cumulative balance: 0
await Promise.all([
TestEthBalance.setBaseBalance(carolsWallet.address as Address, oneEth),
TestEthBalance.setRiverBalance(carolsWallet.address as Address, oneEth),
TestEthBalance.setBaseBalance(alicesWallet.address as Address, 0n),
])

log('expect that alice can join the channel')
// Validate alice can join the channel
Expand All @@ -1223,8 +1255,8 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gate join pass - assets across wallets', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(oneEth))
test('eth balance gate join pass - assets across wallets and networks', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(twoEth))
const {
alice,
bob,
Expand All @@ -1241,8 +1273,12 @@ describe('channelsWithEntitlements', () => {
await linkWallets(aliceSpaceDapp, aliceProvider.wallet, carolProvider.wallet)

// Set wallet balances to 0
await TestEthBalance.setBalance(carolsWallet.address as Address, oneHalfEth)
await TestEthBalance.setBalance(alicesWallet.address as Address, oneHalfEth)
await Promise.all([
TestEthBalance.setBaseBalance(carolsWallet.address as Address, oneHalfEth),
TestEthBalance.setBaseBalance(alicesWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(carolsWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(alicesWallet.address as Address, oneHalfEth),
])

// Validate alice can join the channel
log('expect that alice can join the channel')
Expand All @@ -1255,8 +1291,8 @@ describe('channelsWithEntitlements', () => {
log('Done', Date.now() - doneStart)
})

test('native coin balance gate join fail - insufficient assets across wallets', async () => {
const ruleData = treeToRuleData(nativeCoinBalanceCheckOp(twoEth))
test('eth balance gate join fail - insufficient assets across wallets', async () => {
const ruleData = treeToRuleData(ethBalanceCheckOp(gtTwoEth))
const {
alice,
bob,
Expand All @@ -1275,8 +1311,12 @@ describe('channelsWithEntitlements', () => {
await linkWallets(aliceSpaceDapp, aliceProvider.wallet, carolProvider.wallet)

// Set wallet balances to 0
await TestEthBalance.setBalance(carolsWallet.address as Address, oneHalfEth)
await TestEthBalance.setBalance(alicesWallet.address as Address, oneHalfEth)
await Promise.all([
TestEthBalance.setBaseBalance(carolsWallet.address as Address, oneHalfEth),
TestEthBalance.setBaseBalance(alicesWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(carolsWallet.address as Address, oneHalfEth),
TestEthBalance.setRiverBalance(alicesWallet.address as Address, oneHalfEth),
])

log('expect neither alice nor carol can join the channel')
await expectUserCannotJoinChannel(alice, aliceSpaceDapp, spaceId, channelId!)
Expand Down
Loading

0 comments on commit 425f860

Please sign in to comment.