Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Boilerplate Wagmi & Rainbowkit Adapter #2105

Merged
merged 18 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from 10 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
2,512 changes: 2,443 additions & 69 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 8 additions & 11 deletions packages/fcl-bundle/src/build/get-input-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,14 @@ module.exports = function getInputOptions(package, build) {
.concat(Object.keys(package.dependencies || {}))

let testExternal = id => {
return (
build.type !== "umd" &&
(/@babel\/runtime/g.test(id) ||
external.reduce((state, ext) => {
return (
state ||
(ext instanceof RegExp && ext.test(id)) ||
(typeof ext === "string" && ext === id)
)
}, false))
)
if (build.type !== "umd" && /@babel\/runtime/g.test(id)) return true

for (let ext of external) {
if (ext instanceof RegExp && ext.test(id)) return true
if (typeof ext === "string" && id.startsWith(ext)) return true
}

return false
}

// exclude peer dependencies
Expand Down
5 changes: 4 additions & 1 deletion packages/fcl-ethereum-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@
"@babel/runtime": "^7.25.7",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/hash": "^5.7.0",
"@onflow/fcl": "1.13.4",
"@noble/hashes": "^1.7.1",
"@onflow/rlp": "^1.2.3",
"@walletconnect/jsonrpc-http-connection": "^1.0.8",
"@walletconnect/jsonrpc-provider": "^1.0.14"
},
"peerDependencies": {
"@onflow/fcl": "^1.13.4"
}
}
16 changes: 10 additions & 6 deletions packages/fcl-ethereum-provider/src/accounts/account-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fcl from "@onflow/fcl"
import * as rlp from "@onflow/rlp"
import {CompositeSignature, CurrentUser} from "@onflow/typedefs"
import {CompositeSignature, CurrentUser, Service} from "@onflow/typedefs"
import {
ContractType,
EVENT_IDENTIFIERS,
Expand All @@ -25,7 +25,6 @@ import {
} from "../util/observable"
import {EthSignatureResponse} from "../types/eth"
import {NetworkManager} from "../network/network-manager"
import {formatChainId, getContractAddress} from "../util/eth"
import {createCOATx, getCOAScript, sendTransactionTx} from "../cadence"

export class AccountManager {
Expand All @@ -41,7 +40,8 @@ export class AccountManager {

constructor(
private user: typeof fcl.currentUser,
private networkManager: NetworkManager
private networkManager: NetworkManager,
private service?: Service
) {
// Create an observable from the user
const $user = new Observable<CurrentUser>(subscriber => {
Expand Down Expand Up @@ -88,12 +88,16 @@ export class AccountManager {
.subscribe(this.$addressStore)
}

public async authenticate(): Promise<void> {
return await this.user.authenticate()
public async authenticate(): Promise<string[]> {
await this.user.authenticate({service: this.service})
return this.getAccounts().then(accounts => {
jribbink marked this conversation as resolved.
Show resolved Hide resolved
return accounts
})
}

public async unauthenticate(): Promise<void> {
await this.user.unauthenticate()
await new Promise(resolve => setTimeout(resolve, 0))
jribbink marked this conversation as resolved.
Show resolved Hide resolved
}

private async waitForTxResult(
Expand Down Expand Up @@ -131,7 +135,7 @@ export class AccountManager {
if (error) {
throw error
}
return address
return address || null
}

public async getAccounts(): Promise<string[]> {
Expand Down
44 changes: 22 additions & 22 deletions packages/fcl-ethereum-provider/src/cadence.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Formatting (removing leading/trailing new lines in TX) and fixing the get COA script.

Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import {getContractAddress} from "./util/eth"
import {ContractType} from "./constants"

export const getCOAScript = (chainId: number) => `
import EVM from ${getContractAddress(ContractType.EVM, chainId)}

access(all)
fun main(address: Address): String? {
if let coa = getAuthAccount(address)
.storage
.borrow<&EVM.CadenceOwnedAccount>(from: /storage/evm) {
return coa.address().toString()
}
return nil
}
`

export const createCOATx = (chainId: number) => `
import EVM from ${getContractAddress(ContractType.EVM, chainId)}
export const getCOAScript = (
chainId: number
) => `import EVM from ${getContractAddress(ContractType.EVM, chainId)}

/// Returns the hex encoded address of the COA in the given Flow address
///
access(all) fun main(flowAddress: Address): String? {
return getAuthAccount<auth(BorrowValue) &Account>(flowAddress)
.storage.borrow<&EVM.CadenceOwnedAccount>(from: /storage/evm)
?.address()
?.toString()
?? nil
}`

export const createCOATx = (
chainId: number
) => `import EVM from ${getContractAddress(ContractType.EVM, chainId)}

transaction() {
prepare(signer: auth(SaveValue, IssueStorageCapabilityController, PublishCapability) &Account) {
Expand All @@ -29,11 +30,11 @@ transaction() {
let cap = signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath)
signer.capabilities.publish(cap, at: publicPath)
}
}
`
}`

export const sendTransactionTx = (chainId: number) => `
import EVM from ${getContractAddress(ContractType.EVM, chainId)}
export const sendTransactionTx = (
chainId: number
) => `import EVM from ${getContractAddress(ContractType.EVM, chainId)}

/// Executes the calldata from the signer's COA
transaction(evmContractAddressHex: String, calldata: String, gasLimit: UInt64, value: UInt256) {
Expand All @@ -58,5 +59,4 @@ transaction(evmContractAddressHex: String, calldata: String, gasLimit: UInt64, v
)
assert(callResult.status == EVM.Status.successful, message: "Call failed")
}
}
`
}`
4 changes: 2 additions & 2 deletions packages/fcl-ethereum-provider/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ export enum FlowNetwork {
export const FLOW_CHAINS = {
[FlowNetwork.MAINNET]: {
eip155ChainId: 747,
publicRpcUrl: "https://access.mainnet.nodes.onflow.org",
publicRpcUrl: "https://mainnet.evm.nodes.onflow.org",
},
[FlowNetwork.TESTNET]: {
eip155ChainId: 545,
publicRpcUrl: "https://access.testnet.nodes.onflow.org",
publicRpcUrl: "https://testnet.evm.nodes.onflow.org",
},
}

Expand Down
7 changes: 6 additions & 1 deletion packages/fcl-ethereum-provider/src/create-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {AccountManager} from "./accounts/account-manager"
import {FLOW_CHAINS} from "./constants"
import {Gateway} from "./gateway/gateway"
import {NetworkManager} from "./network/network-manager"
import {Subject} from "./util/observable"

/**
* Create a new FCL Ethereum provider
Expand Down Expand Up @@ -43,7 +44,11 @@ export function createProvider(config: {
)

const networkManager = new NetworkManager(config.config)
const accountManager = new AccountManager(config.user, networkManager)
const accountManager = new AccountManager(
config.user,
networkManager,
config.service
)
const gateway = new Gateway({
...defaultRpcUrls,
...(config.rpcUrls || {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ jest.mock("../network/network-manager")

describe("event dispatcher", () => {
let networkManager: jest.Mocked<NetworkManager>
let accountManager: jest.Mocked<AccountManager>
let $mockChainId: Subject<ChainIdStore>
let accountManager: jest.Mocked<AccountManager>

beforeEach(() => {
jest.clearAllMocks()
Expand Down Expand Up @@ -38,7 +38,7 @@ describe("event dispatcher", () => {
expect(accountManager.subscribe).toHaveBeenCalledWith(expect.any(Function))

// Simulate account change from account manager
subs.forEach(sub => sub(["0x1234"]))
subs.forEach(sub => sub(["1234"]))

expect(listener).toHaveBeenCalled()
expect(listener).toHaveBeenCalledTimes(1)
Expand All @@ -47,7 +47,7 @@ describe("event dispatcher", () => {
eventDispatcher.off("accountsChanged", listener)

// Simulate account change from account manager
subs.forEach(sub => sub(["0x5678"]))
subs.forEach(sub => sub(["5678"]))

expect(listener).toHaveBeenCalledTimes(1)
})
Expand All @@ -68,7 +68,7 @@ describe("event dispatcher", () => {
expect(accountManager.subscribe).toHaveBeenCalledWith(expect.any(Function))

// Simulate account change from account manager
mockMgrSubCb!(["0x1234"])
mockMgrSubCb!(["1234"])

expect(listener).toHaveBeenCalled()
expect(listener).toHaveBeenCalledTimes(1)
Expand All @@ -91,8 +91,8 @@ describe("event dispatcher", () => {
expect(accountManager.subscribe).toHaveBeenCalledWith(expect.any(Function))

// Simulate account change from account manager
mockMgrSubCb!(["0x1234"])
mockMgrSubCb!(["0x5678"])
mockMgrSubCb!(["1234"])
mockMgrSubCb!(["5678"])

expect(listener).toHaveBeenCalled()
expect(listener).toHaveBeenCalledTimes(2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
takeFirst,
} from "../util/observable"
import {formatChainId} from "../util/eth"
import {withPrefix} from "@onflow/fcl"

export class EventDispatcher {
private $emitters: {
Expand All @@ -27,7 +28,7 @@ export class EventDispatcher {
// Emit changes to the accounts as an accountsChanged event
accountsChanged: new Observable(subscriber => {
return accountManager.subscribe(accounts => {
subscriber.next(accounts)
subscriber.next(accounts.map(x => withPrefix(x)))
})
}),
// Emit changes to the chainId as a chainChanged event
Expand Down
4 changes: 2 additions & 2 deletions packages/fcl-ethereum-provider/src/gateway/gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe("gateway", () => {
jest.mocked(HttpConnection).mock.instances[0]
)
expect(HttpConnection).toHaveBeenCalledWith(
"https://access.mainnet.nodes.onflow.org"
"https://mainnet.evm.nodes.onflow.org"
)
})

Expand Down Expand Up @@ -191,7 +191,7 @@ describe("gateway", () => {
jest.mocked(HttpConnection).mock.instances[0]
)
expect(HttpConnection).toHaveBeenCalledWith(
"https://access.testnet.nodes.onflow.org"
"https://testnet.evm.nodes.onflow.org"
)
})
})
4 changes: 2 additions & 2 deletions packages/fcl-ethereum-provider/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export class FclEthereumProvider implements Eip1193Provider {
return result
}

disconnect(): void {
this.acountManager.unauthenticate()
async disconnect(): Promise<void> {
await this.acountManager.unauthenticate()
}

// Listen to events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,21 @@ describe("ethRequestAccounts handler", () => {
it("should call authenticate, updateCOAAddress, and return the manager's accounts", async () => {
accountManagerMock.getAndCreateAccounts.mockResolvedValue(["0x1234..."])

const accounts = await ethRequestAccounts(accountManagerMock)
const accounts = await ethRequestAccounts(accountManagerMock, 747)

expect(accountManagerMock.authenticate).toHaveBeenCalled()
expect(accountManagerMock.getAndCreateAccounts).toHaveBeenCalled()
expect(accounts).toEqual(["0x1234..."])

expect(accountManagerMock.authenticate).toHaveBeenCalled()
expect(accountManagerMock.getAndCreateAccounts).toHaveBeenCalledWith(747)
})

it("should handle empty accounts scenario", async () => {
accountManagerMock.getAndCreateAccounts.mockResolvedValue([])

const accounts = await ethRequestAccounts(accountManagerMock)
const accounts = await ethRequestAccounts(accountManagerMock, 747)

expect(accountManagerMock.authenticate).toHaveBeenCalled()
expect(accountManagerMock.getAndCreateAccounts).toHaveBeenCalled()
expect(accountManagerMock.getAndCreateAccounts).toHaveBeenCalledWith(747)
expect(accounts).toEqual([])
})
})
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import {withPrefix} from "@onflow/fcl"
import {AccountManager} from "../../accounts/account-manager"

export async function ethAccounts(
accountManager: AccountManager
): Promise<string[]> {
return await accountManager.getAccounts()
const accounts = await accountManager.getAccounts()
return accounts.map(x => withPrefix(x))
}

export async function ethRequestAccounts(
accountManager: AccountManager,
chainId: number
): Promise<string[]> {
await accountManager.authenticate()

return await accountManager.getAndCreateAccounts(chainId)
const accounts = await accountManager.getAndCreateAccounts(chainId)
return accounts.map(x => withPrefix(x))
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {formatChainId} from "../../util/eth"
import {NetworkManager} from "../../network/network-manager"
import {ethChainId} from "./eth-chain-id"

jest.mock("../../util/eth", () => ({
Expand All @@ -13,7 +13,7 @@ describe("eth_chainId handler", () => {
}
networkManagerMock.getChainId.mockResolvedValue(747)

const chainId = await ethChainId(networkManagerMock as any)()
const chainId = await ethChainId(networkManagerMock as any)

expect(chainId).toEqual("0x747")
expect(networkManagerMock.getChainId).toHaveBeenCalled()
Expand All @@ -22,10 +22,10 @@ describe("eth_chainId handler", () => {
test("should throw an error if no chain id is available", async () => {
const networkManagerMock = {
getChainId: jest.fn(),
}
} as unknown as jest.Mocked<NetworkManager>
networkManagerMock.getChainId.mockResolvedValue(null)

await expect(ethChainId(networkManagerMock as any)()).rejects.toThrow(
await expect(ethChainId(networkManagerMock)).rejects.toThrow(
"No active chain"
)
expect(networkManagerMock.getChainId).toHaveBeenCalled()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {NetworkManager} from "../../network/network-manager"
import {formatChainId} from "../../util/eth"

export function ethChainId(networkManager: NetworkManager) {
return async function () {
const chainId = await networkManager.getChainId()
if (!chainId) {
throw new Error("No active chain")
}
return formatChainId(chainId)
export async function ethChainId(networkManager: NetworkManager) {
const chainId = await networkManager.getChainId()
if (!chainId) {
throw new Error("No active chain")
}
return formatChainId(chainId)
}
Loading