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

Support Chained Transactions #221

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
6 changes: 3 additions & 3 deletions packages/dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
"lint": "next lint"
},
"dependencies": {
"@alephium/get-extension-wallet": "^1.7.3",
"@alephium/web3": "^1.7.3",
"@alephium/get-extension-wallet": "^1.8.2",
"@alephium/web3": "^1.8.2",
"ethers": "^5.5.1",
"next": "^13.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@alephium/cli": "^1.7.3",
"@alephium/cli": "^1.8.2",
"@types/node": "18.11.18",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/dapp/src/services/token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const mintToken = async (
decimals: 0n,
totalSupply: BigInt(mintAmount)
},
initialAttoAlphAmount: BigInt(1100000000000000000),
initialAttoAlphAmount: web3.MINIMAL_CONTRACT_DEPOSIT,
issueTokenAmount: BigInt(mintAmount),
})
}
Expand Down
8 changes: 4 additions & 4 deletions packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@alephium/get-extension-wallet": "^1.7.3",
"@alephium/get-extension-wallet": "^1.8.2",
"@alephium/ledger-app": "0.6.0",
"@alephium/token-list": "0.0.19",
"@alephium/web3": "^1.7.3",
"@alephium/web3-test": "^1.7.3",
"@alephium/web3-wallet": "^1.7.3",
"@alephium/web3": "^1.8.2",
"@alephium/web3-test": "^1.8.2",
"@alephium/web3-wallet": "^1.8.2",
"@ledgerhq/hw-transport-webusb": "6.29.0",
"@ledgerhq/hw-transport-webhid": "6.29.0",
"@playwright/test": "^1.23.0",
Expand Down
63 changes: 52 additions & 11 deletions packages/extension/src/background/actionHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ActionItem,
ExtQueueItem,
ReviewTransactionResult,
TransactionResult,
} from "../shared/actionQueue/types"
import { MessageType } from "../shared/messages"
import { addNetwork, getNetworks } from "../shared/network"
Expand All @@ -12,7 +13,7 @@ import { assertNever } from "../ui/services/assertNever"
import { analytics } from "./analytics"
import { BackgroundService } from "./background"
import { openUi } from "./openUi"
import { executeTransactionAction } from "./transactions/transactionExecution"
import { executeTransactionsAction, executeTransactionAction } from "./transactions/transactionExecution"
import { transactionWatcher } from "./transactions/transactionWatcher"

export const handleActionApproval = async (
Expand Down Expand Up @@ -49,21 +50,61 @@ export const handleActionApproval = async (
}

case "ALPH_TRANSACTION": {
const { signature: signatureOpt, ...transaction } =
additionalData as ReviewTransactionResult & { signature?: string }
const transactions = additionalData as ReviewTransactionResult[]
try {
const { signature } = await executeTransactionAction(
transaction,
signatureOpt,
background,
transaction.params.networkId,
)
if (transactions.length === 0) {
return {
type: "ALPH_TRANSACTION_FAILED",
data: { actionHash, error: "No transactions to execute" },
}
}

let results: TransactionResult[]
if (transactions.length === 1) {
const { signature: signatureOpt, ...transaction } =
transactions[0] as ReviewTransactionResult & { signature?: string }

const { signature } = await executeTransactionAction(
transaction,
signatureOpt,
background,
transaction.params.networkId,
)

transactionWatcher.refresh()

results = [
{
type: transaction.type,
result: {
...transaction.result,
signature
}
}
] as TransactionResult[]
} else {
const { signatures } = await executeTransactionsAction(
transactions,
background,
transactions[0].params.networkId,
)

transactionWatcher.refresh()
transactionWatcher.refresh()

results = transactions.map((transaction, index) => (
{
type: transaction.type,
result: {
...transaction.result,
signature: signatures[index],
}
}
)) as TransactionResult[]
}

return {
type: "ALPH_TRANSACTION_SUBMITTED",
data: { result: { ...transaction.result, signature }, actionHash },
data: { result: results, actionHash },
}
} catch (error: unknown) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,45 @@ export const executeTransactionAction = async (
)
}

if (account !== undefined) {

addTransaction({
account: account,
hash: transaction.result.txId,
meta: {
request: transaction,
},
})

return { signature: finalSignature }
}

export const executeTransactionsAction = async (
transactions: ReviewTransactionResult[],
{ wallet }: BackgroundService,
networkId: string,
): Promise<{ signatures: string[] }> => {
const signatures: string[] = []
for (const transaction of transactions) {
const account = await wallet.getAccount({
address: transaction.params.signerAddress,
networkId: networkId,
})

const { signature } = await wallet.signAndSubmitUnsignedTx(account, {
signerAddress: account.address,
unsignedTx: transaction.result.unsignedTx,
})

addTransaction({
account: account,
hash: transaction.result.txId,
meta: {
request: transaction,
},
})

signatures.push(signature)
}

return { signature: finalSignature }
return { signatures }
}
122 changes: 107 additions & 15 deletions packages/extension/src/inpage/alephiumWindowObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ import {
KeyType,
NetworkId,
NodeProvider,
SignChainedTxParams,
SignChainedTxResult,
SignDeployContractChainedTxResult,
SignDeployContractTxParams,
SignExecuteScriptChainedTxResult,
SignDeployContractTxResult,
SignExecuteScriptTxParams,
SignExecuteScriptTxResult,
SignMessageParams,
SignMessageResult,
SignTransferChainedTxResult,
SignTransferTxParams,
SignTransferTxResult,
SignUnsignedTxParams,
Expand Down Expand Up @@ -148,7 +153,7 @@ export const alephiumWindowObject: AlephiumWindowObject =
signAndSubmitTransferTx = async (
params: SignTransferTxParams,
): Promise<SignTransferTxResult> => {
const result = (
const [txResult] = (
await this.#executeAlephiumTransaction(
params,
(p, host, networkId, keyType) => ({
Expand All @@ -157,14 +162,14 @@ export const alephiumWindowObject: AlephiumWindowObject =
salt: Date.now().toString(),
}),
)
).result as SignTransferTxResult
return result
).result
return txResult.result as SignTransferTxResult
}

signAndSubmitDeployContractTx = async (
params: SignDeployContractTxParams,
): Promise<SignDeployContractTxResult> => {
const result = (
const [txResult] = (
await this.#executeAlephiumTransaction(
params,
(p, host, networkId, keyType) => ({
Expand All @@ -173,14 +178,15 @@ export const alephiumWindowObject: AlephiumWindowObject =
salt: Date.now().toString(),
}),
)
).result as SignDeployContractTxResult
return { ...result }
).result

return txResult.result as SignDeployContractTxResult
}

signAndSubmitExecuteScriptTx = async (
params: SignExecuteScriptTxParams,
): Promise<SignExecuteScriptTxResult> => {
const result = (
const [txResult] = (
await this.#executeAlephiumTransaction(
params,
(p, host, networkId, keyType) => ({
Expand All @@ -189,14 +195,14 @@ export const alephiumWindowObject: AlephiumWindowObject =
salt: Date.now().toString(),
}),
)
).result as SignExecuteScriptTxResult
return result
).result
return txResult.result as SignExecuteScriptTxResult
}

signAndSubmitUnsignedTx = async (
params: SignUnsignedTxParams,
): Promise<SignUnsignedTxResult> => {
const result = (
const [txResult] = (
await this.#executeAlephiumTransaction(
params,
(p, host, networkId, keyType) => ({
Expand All @@ -205,8 +211,96 @@ export const alephiumWindowObject: AlephiumWindowObject =
salt: Date.now().toString(),
}),
)
).result as SignUnsignedTxResult
return result
).result

return txResult.result as SignUnsignedTxResult
}

signAndSubmitChainedTx = async (
params: SignChainedTxParams[],
): Promise<SignChainedTxResult[]> => {
if (params.length === 0) {
throw Error("Empty transaction params")
}

const transactionParamz: TransactionParams[] = params.map(param => {
const paramsType = param.type
const salt = Date.now().toString()
switch (paramsType) {
case 'Transfer':
return {
type: 'TRANSFER',
params: { networkId: this.connectedNetworkId, ...param },
salt
}
case 'DeployContract':
return {
type: 'DEPLOY_CONTRACT',
params: { networkId: this.connectedNetworkId, ...param },
salt
}
case 'ExecuteScript':
return {
type: 'EXECUTE_SCRIPT',
params: { networkId: this.connectedNetworkId, ...param },
salt
}
default:
throw new Error(`Unsupported transaction type: ${paramsType}`);
}
})

sendMessage({ type: "ALPH_EXECUTE_TRANSACTION", data: transactionParamz })

const { actionHash } = await waitForMessage(
"ALPH_EXECUTE_TRANSACTION_RES",
USER_ACTION_TIMEOUT,
)

sendMessage({ type: "ALPH_OPEN_UI" })

const result = await Promise.race([
waitForMessage(
"ALPH_TRANSACTION_SUBMITTED",
USER_ACTION_TIMEOUT_LONGER,
(x) => x.data.actionHash === actionHash,
),
waitForMessage(
"ALPH_TRANSACTION_FAILED",
USER_ACTION_TIMEOUT,
(x) => x.data.actionHash === actionHash,
)
.then((res) => res)
.catch(() => {
const error = "User action time out"
sendMessage({
type: "ALPH_TRANSACTION_FAILED",
data: { actionHash, error },
})
return { error }
}),
])

if ("error" in result) {
throw Error(result.error)
}

const signedChainedTxResult = result.result.map(txResult => {
const txResultType = txResult.type
switch (txResultType) {
case 'TRANSFER':
return { type: 'Transfer', ...txResult.result } as SignTransferChainedTxResult;
case 'DEPLOY_CONTRACT':
return { type: 'DeployContract', ...txResult.result } as SignDeployContractChainedTxResult;
case 'EXECUTE_SCRIPT':
return { type: 'ExecuteScript', ...txResult.result } as SignExecuteScriptChainedTxResult;
default:
throw new Error(`Unsupported transaction type: ${txResultType}`);
}
}
)

return signedChainedTxResult;
}

signUnsignedTx = async (
Expand All @@ -218,7 +312,6 @@ export const alephiumWindowObject: AlephiumWindowObject =
throw new Error("Invalid unsigned tx")
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sendMessage({
type: "ALPH_SIGN_UNSIGNED_TX",
data: {
Expand Down Expand Up @@ -368,15 +461,14 @@ export const alephiumWindowObject: AlephiumWindowObject =
) {
this.#checkParams(params)

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const data = dataBuilder(
params,
window.location.host,
this.#connectedNetworkId!,
this.connectedAccount!.keyType,
)

sendMessage({ type: "ALPH_EXECUTE_TRANSACTION", data })
sendMessage({ type: "ALPH_EXECUTE_TRANSACTION", data: [data] })
const { actionHash } = await waitForMessage(
"ALPH_EXECUTE_TRANSACTION_RES",
USER_ACTION_TIMEOUT,
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/src/shared/actionQueue/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export type ActionItem =
}
| {
type: "ALPH_TRANSACTION"
payload: TransactionParams
payload: TransactionParams[]
}
| {
type: "ALPH_SIGN_MESSAGE"
Expand Down
Loading
Loading