Skip to content

Commit

Permalink
feat: extract splitterFactory address to the object and expose chain …
Browse files Browse the repository at this point in the history
…ID (#472)
  • Loading branch information
vojtechsimetka authored Oct 13, 2023
1 parent fe24f5c commit dc9dbf8
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 29 deletions.
22 changes: 10 additions & 12 deletions src/lib/adapters/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,28 @@ interface BlockchainExplorer {

interface BlockchainNetwork {
name: string
chainId: bigint
provider: string
explorer?: BlockchainExplorer
nativeToken: Token
tokens?: Token[]
objects: {
splitterFactory: string
}
}

const localBlockchain: BlockchainNetwork = {
name: 'Local testnet',
chainId: 31337n,
provider: 'http://127.0.0.1:8545',
nativeToken: {
name: 'Test Ether',
symbol: 'ETH',
decimals: 18,
image: 'https://s2.coinmarketcap.com/static/img/coins/64x64/1027.png',
},
objects: {
splitterFactory: '0x0',
},
}

const chiadoBlockchain: BlockchainNetwork = {
name: 'Chiado testnet',
chainId: 10200n,
provider: 'https://rpc.chiado.apyos.dev/',
explorer: {
name: 'Blockscout',
Expand All @@ -63,13 +60,11 @@ const chiadoBlockchain: BlockchainNetwork = {
address: '0x19C653Da7c37c66208fbfbE8908A5051B57b4C70',
},
],
objects: {
splitterFactory: '0x941DDB22a33FC753d3E7b82cc34c47Ee605e60a3',
},
}

const gnosisBlockchain: BlockchainNetwork = {
name: 'Gnosis',
chainId: 100n,
provider: 'https://gnosis-erigon.apyos.dev/',
explorer: {
name: 'Blockscout',
Expand All @@ -90,9 +85,6 @@ const gnosisBlockchain: BlockchainNetwork = {
address: '0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb',
},
],
objects: {
splitterFactory: '0x99873C280c0c4A5460AB781e4bcD06f6B5f35717',
},
}

function getBlockchainNetwork(): BlockchainNetwork {
Expand All @@ -118,6 +110,12 @@ export function getProvider(): Provider {
return provider
}

export async function getChainId(): Promise<bigint> {
const provider = getProvider()
const { chainId } = await provider.getNetwork()
return chainId
}

export async function sendTransaction(
wallet: BaseWallet,
to: string,
Expand Down
4 changes: 3 additions & 1 deletion src/lib/adapters/waku/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { walletStore } from '$lib/stores/wallet'
import { SafeWaku } from './safe-waku'
import type { TokenAmount } from '$lib/objects/schemas'
import { DEFAULT_FIAT_SYMBOL, exchangeStore } from '$lib/stores/exchangeRates'
import { balanceStore } from '$lib/stores/balances'

const MAX_MESSAGES = 100

Expand Down Expand Up @@ -148,6 +149,7 @@ async function executeOnDataMessage(
chat?.name ?? users.find((u) => u.address !== myProfile.address)?.name ?? 'Unknown'
const args: WakuObjectArgs = {
...context,
chainId: defaultBlockchainNetwork.chainId,
chatName,
chatId,
objectId: dataMessage.objectId,
Expand All @@ -156,7 +158,7 @@ async function executeOnDataMessage(
profile: myProfile,
exchangeRates: get(exchangeStore).exchange,
fiatSymbol: DEFAULT_FIAT_SYMBOL,
tokens: defaultBlockchainNetwork.tokens?.map((t) => ({ ...t, amount: 0n })) || [],
tokens: get(balanceStore).balances,
}
await descriptor.onMessage(dataMessage, args)
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/objects/chat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import routes from '$lib/routes'
import { chats } from '$lib/stores/chat'
import { DEFAULT_FIAT_SYMBOL, exchangeStore } from '$lib/stores/exchangeRates'
import { defaultBlockchainNetwork } from '$lib/adapters/transaction'
export let message: DataMessage
export let users: User[]
Expand Down Expand Up @@ -52,6 +53,7 @@
const chatName =
chat?.name ?? users.find((u) => u.address !== userProfile.address)?.name ?? 'Unknown'
args = {
chainId: defaultBlockchainNetwork.chainId,
chatId,
objectId: message.objectId,
instanceId: message.instanceId,
Expand Down
2 changes: 2 additions & 0 deletions src/lib/objects/external/iframe.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import adapters from '$lib/adapters'
import { walletStore } from '$lib/stores/wallet'
import { DEFAULT_FIAT_SYMBOL, exchangeStore } from '$lib/stores/exchangeRates'
import { defaultBlockchainNetwork } from '$lib/adapters/transaction'
// TODO: This needs escaping for the CSP
const getIframeSource = (object: LoadedObject): string => {
Expand Down Expand Up @@ -100,6 +101,7 @@
const iframeContextChange: IframeContextChange = {
type: 'iframe-context-change',
state: {
chainId: defaultBlockchainNetwork.chainId,
chatId,
chatName,
objectId,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/objects/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export interface WakuObjectAdapter {

export type JSONPrimitive = string | number | boolean | null
export type JSONObject = { [key: symbol]: JSONValue }
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface JSONArray extends Array<JSONValue> {}
export type JSONArray = Array<JSONValue>

export type JSONValue = JSONPrimitive | JSONObject | JSONArray

export type JSONSerializable = JSONValue

export interface WakuObjectState {
readonly chainId: bigint
readonly chatId: string
readonly objectId: string
readonly instanceId: string
Expand Down
35 changes: 26 additions & 9 deletions src/lib/objects/split/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@ import { Interface, type Provider } from 'ethers'
import type { GetContract } from './types'
import splitterFactoryAbi from './contracts/abis/splitter-factory.json'
import splitterAbi from './contracts/abis/splitter.json'
import { defaultBlockchainNetwork } from '$lib/adapters/transaction'
import type { Balance } from './schemas'
import type { Splitter, SplitterFactory } from './contracts/types'

const splitterFactoryAddress = defaultBlockchainNetwork.objects.splitterFactory
function getSplitterFactoryAddress(chainId: bigint) {
switch (chainId) {
case 100n:
return '0x99873C280c0c4A5460AB781e4bcD06f6B5f35717'
case 10200n:
return '0x941DDB22a33FC753d3E7b82cc34c47Ee605e60a3'
default:
throw new Error('Unsupported chainId')
}
}

function getSplitterContract(getContract: GetContract, splitterAddress: string): Splitter {
return getContract(splitterAddress, new Interface(splitterAbi)) as unknown as Splitter
}

function getSplitterContractFactory(getContract: GetContract): SplitterFactory {
function getSplitterContractFactory(getContract: GetContract, chainId: bigint): SplitterFactory {
return getContract(
splitterFactoryAddress,
getSplitterFactoryAddress(chainId),
new Interface(splitterFactoryAbi),
) as unknown as SplitterFactory
}
Expand All @@ -35,11 +43,12 @@ function sleep(ms: number) {

export async function createSplitterContract(
getContract: GetContract,
chainId: bigint,
members: string[],
token = '0x0000000000000000000000000000000000000000',
metadata = '0x0000000000000000000000000000000000000000',
): Promise<string> {
const splitterFactory = getSplitterContractFactory(getContract)
const splitterFactory = getSplitterContractFactory(getContract, chainId)

// TODO: this should be `once` with appropriate filter instead of `on`
const events: { address: string; txHash: string }[] = []
Expand Down Expand Up @@ -80,11 +89,12 @@ export async function createSplitterContract(

export async function estimateCreateSplitterContract(
getContract: GetContract,
chainId: bigint,
members: string[],
token = '0x0000000000000000000000000000000000000000',
metadata = '0x0000000000000000000000000000000000000000',
): Promise<bigint> {
const splitterFactory = getSplitterContractFactory(getContract)
const splitterFactory = getSplitterContractFactory(getContract, chainId)

const gasEstimate: bigint = await splitterFactory.create.estimateGas(metadata, token, members)

Expand All @@ -94,8 +104,11 @@ export async function estimateCreateSplitterContract(
return calculateFee(provider, gasEstimate)
}

export async function getMasterSplitterContractAddress(getContract: GetContract): Promise<string> {
const splitterFactory = getSplitterContractFactory(getContract)
export async function getMasterSplitterContractAddress(
getContract: GetContract,
chainId: bigint,
): Promise<string> {
const splitterFactory = getSplitterContractFactory(getContract, chainId)
return await splitterFactory.masterSplitter()
}

Expand All @@ -119,6 +132,7 @@ export async function addExpense(

export async function estimateAddExpense(
getContract: GetContract,
chainId: bigint,
splitterAddress: string | undefined,
amount: bigint,
from: string,
Expand All @@ -134,7 +148,10 @@ export async function estimateAddExpense(
// This is worse case estimateAddExpense cost
// we can not do it by estimating the transaction because we don't have the splitter contract deployed and the transaction would fail in the master splitter contract
gasEstimate = 47530n + 9000n * BigInt(members.length - 2)
splitter = getSplitterContract(getContract, await getMasterSplitterContractAddress(getContract))
splitter = getSplitterContract(
getContract,
await getMasterSplitterContractAddress(getContract, chainId),
)
}

const provider = splitter.runner?.provider
Expand Down
1 change: 1 addition & 0 deletions src/lib/objects/split/standalone.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
{images}
{token}
{nativeToken}
chainId={args.chainId}
profile={args.profile}
chatName={args.chatName}
send={args.send}
Expand Down
14 changes: 11 additions & 3 deletions src/lib/objects/split/views/summary.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
export let instanceId: string
export let token: Token
export let nativeToken: TokenAmount
export let chainId: bigint
export let send: (message: DataMessage) => Promise<void>
export let exitObject: () => void
export let getContract: GetContract
Expand All @@ -48,9 +49,16 @@
let splitContractAddress = splitterAddress
if (!splitContractAddress) {
fee = await estimateCreateSplitterContract(getContract, users)
fee = await estimateCreateSplitterContract(getContract, chainId, users)
}
fee += await estimateAddExpense(getContract, splitContractAddress, amnt, profile.address, users)
fee += await estimateAddExpense(
getContract,
chainId,
splitContractAddress,
amnt,
profile.address,
users,
)
return fee
}
Expand All @@ -65,7 +73,7 @@
try {
let splitContractAddress = splitterAddress
if (!splitContractAddress) {
splitContractAddress = await createSplitterContract(getContract, users)
splitContractAddress = await createSplitterContract(getContract, chainId, users)
}
let amnt = toBigInt(amount, token.decimals)
Expand Down
2 changes: 2 additions & 0 deletions src/lib/objects/ui.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import type { HDNodeWallet } from 'ethers/lib.commonjs'
import type { TokenAmount } from './schemas'
import { DEFAULT_FIAT_SYMBOL, exchangeStore } from '$lib/stores/exchangeRates'
import { defaultBlockchainNetwork } from '$lib/adapters/transaction'
export let objectId: string
export let instanceId: string
Expand Down Expand Up @@ -66,6 +67,7 @@
const chatName =
chat?.name ?? users.find((u) => u.address !== userProfile.address)?.name ?? 'Unknown'
args = {
chainId: defaultBlockchainNetwork.chainId,
chatId,
objectId,
instanceId,
Expand Down
27 changes: 25 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
import { walletStore } from '$lib/stores/wallet'
import { theme } from '$lib/stores/theme'
import { exchangeStore } from '$lib/stores/exchangeRates'
import { defaultBlockchainNetwork, getChainId } from '$lib/adapters/transaction'
import Container from '$lib/components/container.svelte'
import Loading from '$lib/components/loading.svelte'
let unsubscribeWalletStore: (() => void) | undefined = undefined
let unsubscribeExchangeStore: (() => void) | undefined = undefined
let loading = true
let error: string | undefined = undefined
onMount(() => {
onMount(async () => {
unsubscribeWalletStore = walletStore.subscribe(({ wallet }) => {
if (wallet) {
adapter.onLogIn(wallet)
Expand All @@ -29,6 +34,13 @@
const interval = setInterval(exchangeStore.update, MINUTE)
unsubscribeExchangeStore = () => clearInterval(interval)
// Ensures the blockchain connection is on the correct network
const chainId = await getChainId()
if (defaultBlockchainNetwork.chainId !== chainId) {
error = `Incorrect blockchain connection. Got chain ID ${chainId.toString()}, expected ${defaultBlockchainNetwork.chainId.toString()}`
}
loading = false
})
onDestroy(() => {
Expand All @@ -41,7 +53,18 @@
</script>

<div class="root">
<slot />
{#if loading}
<Container align="center" gap={6} justify="center">
<Loading />
</Container>
{:else if error !== undefined}
<!-- FIXME: use the error component-->
<Container align="center" gap={6} justify="center" padX={24}>
<h2>{error}</h2>
</Container>
{:else}
<slot />
{/if}
</div>

<style>
Expand Down

1 comment on commit dc9dbf8

@vercel
Copy link

@vercel vercel bot commented on dc9dbf8 Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.