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

Passkey support #611

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ jobs:
- uses: ./.github/actions/install-dependencies
- run: pnpm --filter network test

tests-passkeys:
name: Run passkeys tests
runs-on: ubuntu-latest
needs: [install]
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/install-dependencies
- run: pnpm --filter passkeys test

tests-provider:
name: Run provider tests
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@0xsequence/marketplace": "workspace:*",
"@0xsequence/metadata": "workspace:*",
"@0xsequence/network": "workspace:*",
"@0xsequence/passkeys": "workspace:*",
"@0xsequence/provider": "workspace:*",
"@0xsequence/relayer": "workspace:*",
"@0xsequence/utils": "workspace:*",
Expand Down
19 changes: 19 additions & 0 deletions packages/abi/src/wallet/eternalFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

export const abi = [
{
type: 'function',
name: 'deployEternal',
constant: false,
inputs: [
{
type: 'address'
},
{
type: 'bytes32'
}
],
outputs: [],
payable: true,
stateMutability: 'payable'
}
]
4 changes: 3 additions & 1 deletion packages/abi/src/wallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as mainModule from './mainModule'
import * as mainModuleUpgradable from './mainModuleUpgradable'
import * as sequenceUtils from './sequenceUtils'
import * as requireFreshSigner from './libs/requireFreshSigners'
import * as eternalFactory from './eternalFactory'

export const walletContracts = {
erc6492,
Expand All @@ -15,5 +16,6 @@ export const walletContracts = {
mainModule,
mainModuleUpgradable,
sequenceUtils,
requireFreshSigner
requireFreshSigner,
eternalFactory
}
26 changes: 15 additions & 11 deletions packages/account/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export type AccountOptions = {
projectAccessKey?: string
}

export type AccountCoders = {
signature: commons.signature.SignatureCoder
config: commons.config.ConfigCoder
}

export interface PreparedTransactions {
transactions: commons.transaction.SimulatedTransaction[]
flatDecorated: commons.transaction.Transaction[]
Expand Down Expand Up @@ -164,10 +169,7 @@ export class Account {
return this.migrator.lastMigration().version
}

get coders(): {
signature: commons.signature.SignatureCoder
config: commons.config.ConfigCoder
} {
get coders(): AccountCoders {
const lastMigration = this.migrator.lastMigration()

return {
Expand Down Expand Up @@ -244,7 +246,7 @@ export class Account {
chainId: ethers.BigNumberish,
context: commons.context.WalletContext,
config: commons.config.Config,
coders: typeof this.coders
coders: AccountCoders,
): Wallet {
const isNetworkZero = BigInt(chainId) === 0n
return new Wallet({
Expand Down Expand Up @@ -505,9 +507,8 @@ export class Account {

const wallet = this.walletFor(chainId, status.original.context, allOfAll, this.coders)
const signature = await wallet.signDigest(digest)

const decoded = this.coders.signature.decode(signature)
const signatures = this.coders.signature.signaturesOfDecoded(decoded)
const signatures = this.coders.signature.signaturesOf(decoded)

if (signatures.length === 0) {
throw new Error('No signatures found')
Expand All @@ -516,12 +517,15 @@ export class Account {
return this.tracker.saveWitnesses({ wallet: this.address, digest, chainId, signatures })
}

async publishWitness(): Promise<void> {
async publishWitness(chainId: ethers.BigNumberish = 0, referenceChainId?: ethers.BigNumberish): Promise<void> {
const digest = ethers.id(`This is a Sequence account woo! ${Date.now()}`)
const signature = await this.signDigest(digest, 0, false)
// Apply ERC-6492 to undeployed children
const signature = await this.signDigest(digest, 0, false, 'ignore', {chainId, referenceChainId, cantValidateBehavior: "eip6492"})
const decoded = this.coders.signature.decode(signature)
const signatures = this.coders.signature.signaturesOfDecoded(decoded)
return this.tracker.saveWitnesses({ wallet: this.address, digest, chainId: 0, signatures })
const recovered = await this.coders.signature.recover(decoded, { digest, chainId, address: this.address }, undefined, 'ignore')
const signatures = this.coders.signature.signaturesOf(recovered.config)
const signaturesWithReferenceChainId = signatures.map(s => ({...s, referenceChainId}))
return this.tracker.saveWitnesses({ wallet: this.address, digest, chainId, signatures: signaturesWithReferenceChainId })
}

async signDigest(
Expand Down
93 changes: 86 additions & 7 deletions packages/api/src/api.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable */
// sequence-api v0.4.0 470a0f88ea399c2a57ff8c22da54358c033ed5f0
// sequence-api v0.4.0 bbe47e0331a18438fe5f7fe590a4b7b4e5102738
// --
// Code generated by webrpc-gen@v0.18.7 with typescript generator. DO NOT EDIT.
// Code generated by webrpc-gen@v0.20.3 with typescript generator. DO NOT EDIT.
//
// webrpc-gen -schema=api.ridl -target=typescript -client -out=./clients/api.gen.ts

Expand All @@ -12,7 +12,7 @@ export const WebRPCVersion = 'v1'
export const WebRPCSchemaVersion = 'v0.4.0'

// Schema hash generated from your RIDL schema
export const WebRPCSchemaHash = '470a0f88ea399c2a57ff8c22da54358c033ed5f0'
export const WebRPCSchemaHash = 'bbe47e0331a18438fe5f7fe590a4b7b4e5102738'

//
// Types
Expand Down Expand Up @@ -78,6 +78,12 @@ export interface SequenceContext {
utils: string
}

export interface PublicKey {
id: string
x: string
y: string
}

export interface User {
address: string
username: string
Expand Down Expand Up @@ -362,7 +368,7 @@ export interface SwapPrice {
currencyAddress: string
currencyBalance: string
price: string
to: string
maxPrice: string
transactionValue: string
}

Expand Down Expand Up @@ -445,6 +451,8 @@ export interface API {
headers?: object,
signal?: AbortSignal
): Promise<SendPasswordlessLinkReturn>
registerPublicKey(args: RegisterPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise<RegisterPublicKeyReturn>
getPublicKey(args: GetPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise<GetPublicKeyReturn>
friendList(args: FriendListArgs, headers?: object, signal?: AbortSignal): Promise<FriendListReturn>
getFriendByAddress(args: GetFriendByAddressArgs, headers?: object, signal?: AbortSignal): Promise<GetFriendByAddressReturn>
searchFriends(args: SearchFriendsArgs, headers?: object, signal?: AbortSignal): Promise<SearchFriendsReturn>
Expand Down Expand Up @@ -539,6 +547,7 @@ export interface API {
headers?: object,
signal?: AbortSignal
): Promise<ValidateWaaSVerificationNonceReturn>
getSwapPrice(args: GetSwapPriceArgs, headers?: object, signal?: AbortSignal): Promise<GetSwapPriceReturn>
getSwapPrices(args: GetSwapPricesArgs, headers?: object, signal?: AbortSignal): Promise<GetSwapPricesReturn>
getSwapQuote(args: GetSwapQuoteArgs, headers?: object, signal?: AbortSignal): Promise<GetSwapQuoteReturn>
listCurrencyGroups(headers?: object, signal?: AbortSignal): Promise<ListCurrencyGroupsReturn>
Expand Down Expand Up @@ -635,6 +644,20 @@ export interface SendPasswordlessLinkArgs {
export interface SendPasswordlessLinkReturn {
status: boolean
}
export interface RegisterPublicKeyArgs {
publicKey: PublicKey
}

export interface RegisterPublicKeyReturn {
status: boolean
}
export interface GetPublicKeyArgs {
id: string
}

export interface GetPublicKeyReturn {
publicKey: PublicKey
}
export interface FriendListArgs {
nickname?: string
page?: Page
Expand Down Expand Up @@ -971,8 +994,6 @@ export interface RemoveLinkedWalletArgs {
parentWalletMessage: string
parentWalletSignature: string
linkedWalletAddress: string
linkedWalletMessage: string
linkedWalletSignature: string
signatureChainId: string
}

Expand All @@ -997,11 +1018,23 @@ export interface ValidateWaaSVerificationNonceArgs {
export interface ValidateWaaSVerificationNonceReturn {
walletAddress: string
}
export interface GetSwapPriceArgs {
buyCurrencyAddress: string
sellCurrencyAddress: string
buyAmount: string
chainId: number
slippagePercentage?: number
}

export interface GetSwapPriceReturn {
swapPrice: SwapPrice
}
export interface GetSwapPricesArgs {
userAddress: string
buyCurrencyAddress: string
buyAmount: string
chainId: number
slippagePercentage?: number
}

export interface GetSwapPricesReturn {
Expand All @@ -1014,6 +1047,7 @@ export interface GetSwapQuoteArgs {
buyAmount: string
chainId: number
includeApprove: boolean
slippagePercentage?: number
}

export interface GetSwapQuoteReturn {
Expand Down Expand Up @@ -1086,7 +1120,7 @@ export class API implements API {
protected path = '/rpc/API/'

constructor(hostname: string, fetch: Fetch) {
this.hostname = hostname
this.hostname = hostname.replace(/\/*$/, '')
this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init)
}

Expand Down Expand Up @@ -1224,6 +1258,36 @@ export class API implements API {
)
}

registerPublicKey = (args: RegisterPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise<RegisterPublicKeyReturn> => {
return this.fetch(this.url('RegisterPublicKey'), createHTTPRequest(args, headers, signal)).then(
res => {
return buildResponse(res).then(_data => {
return {
status: <boolean>_data.status
}
})
},
error => {
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` })
}
)
}

getPublicKey = (args: GetPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise<GetPublicKeyReturn> => {
return this.fetch(this.url('GetPublicKey'), createHTTPRequest(args, headers, signal)).then(
res => {
return buildResponse(res).then(_data => {
return {
publicKey: <PublicKey>_data.publicKey
}
})
},
error => {
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` })
}
)
}

friendList = (args: FriendListArgs, headers?: object, signal?: AbortSignal): Promise<FriendListReturn> => {
return this.fetch(this.url('FriendList'), createHTTPRequest(args, headers, signal)).then(
res => {
Expand Down Expand Up @@ -1999,6 +2063,21 @@ export class API implements API {
)
}

getSwapPrice = (args: GetSwapPriceArgs, headers?: object, signal?: AbortSignal): Promise<GetSwapPriceReturn> => {
return this.fetch(this.url('GetSwapPrice'), createHTTPRequest(args, headers, signal)).then(
res => {
return buildResponse(res).then(_data => {
return {
swapPrice: <SwapPrice>_data.swapPrice
}
})
},
error => {
throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` })
}
)
}

getSwapPrices = (args: GetSwapPricesArgs, headers?: object, signal?: AbortSignal): Promise<GetSwapPricesReturn> => {
return this.fetch(this.url('GetSwapPrices'), createHTTPRequest(args, headers, signal)).then(
res => {
Expand Down
15 changes: 10 additions & 5 deletions packages/auth/src/services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Account } from '@0xsequence/account'
import { SequenceAPIClient } from '@0xsequence/api'
import { GetAuthToken2Return, GetAuthTokenReturn, SequenceAPIClient } from '@0xsequence/api'
import { ETHAuth, Proof } from '@0xsequence/ethauth'
import { Indexer, SequenceIndexer, SequenceIndexerGateway } from '@0xsequence/indexer'
import { SequenceMetadata } from '@0xsequence/metadata'
Expand Down Expand Up @@ -101,7 +101,7 @@ export class Services {
}
}

auth(maxTries: number = 5): Promise<SequenceAPIClient> {
auth(maxTries: number = 5, referenceChainId?: ChainIdLike): Promise<SequenceAPIClient> {
if (this._initialAuthRequest) return this._initialAuthRequest

this._initialAuthRequest = (async () => {
Expand All @@ -111,7 +111,7 @@ export class Services {
let jwtAuth: string | undefined
for (let i = 1; ; i++) {
try {
jwtAuth = (await this.getJWT(true)).token
jwtAuth = (await this.getJWT(true, referenceChainId)).token
break
} catch (error) {
if (i === maxTries) {
Expand All @@ -127,7 +127,7 @@ export class Services {
return this._initialAuthRequest
}

private async getJWT(tryAuth: boolean): Promise<SessionJWT> {
private async getJWT(tryAuth: boolean, referenceChainId?: ChainIdLike): Promise<SessionJWT> {
const url = this.settings.sequenceApiUrl
if (!url) throw Error('No sequence api url')

Expand Down Expand Up @@ -156,7 +156,12 @@ export class Services {
.then(async proofString => {
const api = new SequenceAPIClient(url)

const authResp = await api.getAuthToken({ ewtString: proofString })
let authResp: GetAuthToken2Return | GetAuthTokenReturn
if (referenceChainId) {
authResp = await api.getAuthToken2({ ewtString: proofString, chainID: referenceChainId?.toString() })
} else {
authResp = await api.getAuthToken({ ewtString: proofString })
}

if (authResp?.status === true && authResp.jwtToken.length !== 0) {
return authResp.jwtToken
Expand Down
4 changes: 2 additions & 2 deletions packages/auth/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class Session {

// sign a digest and send it to the tracker
// otherwise the tracker will not know about this account
await account.publishWitness()
await account.publishWitness(undefined, referenceChainId)

// safety check, the remove tracker should be able to find
// this account for the reference signer
Expand All @@ -303,7 +303,7 @@ export class Session {

if (services) {
servicesObj = new Services(account, services)
servicesObj.auth() // fire and forget
servicesObj.auth(undefined, referenceChainId) // fire and forget

servicesObj.onAuth(result => {
if (result.status === 'fulfilled') {
Expand Down
Loading
Loading