Skip to content

Commit

Permalink
Add support for BCS transaction (#111)
Browse files Browse the repository at this point in the history
* support bcs transaction

* add aptos wallet plugin example

* update pnpm

* add changeset
  • Loading branch information
0xmaayan authored Apr 11, 2023
1 parent 075d77d commit 06a2e0d
Show file tree
Hide file tree
Showing 13 changed files with 758 additions and 48 deletions.
8 changes: 8 additions & 0 deletions .changeset/fresh-clouds-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@aptos-labs/wallet-adapter-nextjs-example": major
"@aptos-labs/aptos-wallet-plugin": major
"@aptos-labs/wallet-adapter-core": major
"@aptos-labs/wallet-adapter-react": major
---

Add support to submit BCS transaction
44 changes: 43 additions & 1 deletion apps/nextjs-example/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AptosClient, Types } from "aptos";
import { AptosClient, BCS, TxnBuilderTypes, Types } from "aptos";
import { useWallet } from "@aptos-labs/wallet-adapter-react";
import { WalletSelector } from "@aptos-labs/wallet-adapter-ant-design";
import { WalletConnector } from "@aptos-labs/wallet-adapter-mui-design";
Expand Down Expand Up @@ -36,6 +36,37 @@ export default function App() {
const [successAlertMessage, setSuccessAlertMessage] = useState<string>("");
const [errorAlertMessage, setErrorAlertMessage] = useState<string>("");

const onSignAndSubmitBCSTransaction = async () => {
const token = new TxnBuilderTypes.TypeTagStruct(
TxnBuilderTypes.StructTag.fromString("0x1::aptos_coin::AptosCoin")
);
const entryFunctionBCSPayload =
new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
"0x1::coin",
"transfer",
[token],
[
BCS.bcsToBytes(
TxnBuilderTypes.AccountAddress.fromHex(account!.address)
),
BCS.bcsSerializeUint64(2),
]
)
);

try {
const response = await signAndSubmitTransaction(entryFunctionBCSPayload);
await aptosClient.waitForTransaction(response?.hash || "");
setSuccessAlertMessage(
`https://explorer.aptoslabs.com/txn/${response?.hash}`
);
} catch (error: any) {
console.log("error", error);
setErrorAlertMessage(error);
}
};

const onSignAndSubmitTransaction = async () => {
const payload: Types.TransactionPayload = {
type: "entry_function_payload",
Expand Down Expand Up @@ -173,6 +204,17 @@ export default function App() {
>
Sign and submit transaction
</button>
<button
className={`bg-blue-500 text-white font-bold py-2 px-4 rounded mr-4 ${
!connected
? "opacity-50 cursor-not-allowed"
: "hover:bg-blue-700"
}`}
onClick={onSignAndSubmitBCSTransaction}
disabled={!connected}
>
Sign and submit BCS transaction
</button>
<button
className={`bg-blue-500 text-white font-bold py-2 px-4 rounded mr-4 ${
!connected
Expand Down
4 changes: 4 additions & 0 deletions packages/aptos-wallet-plugin-example/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["adapter"],
};
1 change: 1 addition & 0 deletions packages/aptos-wallet-plugin-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
4 changes: 4 additions & 0 deletions packages/aptos-wallet-plugin-example/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.turbo
.eslintrc.js
tsconfig.json
35 changes: 35 additions & 0 deletions packages/aptos-wallet-plugin-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@aptos-labs/aptos-wallet-plugin",
"version": "0.1.3",
"description": "Aptos plugin to use with Aptos Wallet Adapter",
"author": "Aptos",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"license": "Apache-2.0",
"exports": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
"dev": "tsup src/index.ts --format esm,cjs --watch --dts",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
"test": "jest"
},
"dependencies": {
"@aptos-labs/wallet-adapter-core": "workspace:*",
"aptos": "^1.4.0"
},
"devDependencies": {
"@aptos-labs/eslint-config-adapter": "workspace:*",
"@aptos-labs/wallet-adapter-tsconfig": "workspace:*",
"@swc/core": "^1.3.47",
"@types/jest": "^29.2.3",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"tsup": "^6.5.0",
"typescript": "^4.9.3"
}
}
147 changes: 147 additions & 0 deletions packages/aptos-wallet-plugin-example/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import type {
AptosWalletErrorResult,
NetworkName,
PluginProvider,
AccountInfo,
AdapterPlugin,
NetworkInfo,
SignMessagePayload,
SignMessageResponse,
WalletName,
TransactionPayload,
} from "@aptos-labs/wallet-adapter-core";
import { Types } from "aptos";

interface AptosWindow extends Window {
aptos?: PluginProvider;
}

declare const window: AptosWindow;

export const AptosWalletName = "Aptos" as WalletName<"Aptos">;

export class AptosWallet implements AdapterPlugin {
readonly name = AptosWalletName;
readonly url = "https://chrome.google.com/webstore/detail/aptos-wallet";
readonly icon =
"data:image/png;base64,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";

provider: PluginProvider | undefined =
typeof window !== "undefined" ? window.aptos : undefined;

async connect(): Promise<AccountInfo> {
try {
const addressInfo = await this.provider?.connect();
if (!addressInfo) throw `${AptosWalletName} Address Info Error`;
return addressInfo;
} catch (error: any) {
throw error;
}
}

async account(): Promise<AccountInfo> {
const response = await this.provider?.account();
if (!response) throw `${AptosWalletName} Account Error`;
return response;
}

async disconnect(): Promise<void> {
try {
await this.provider?.disconnect();
} catch (error: any) {
throw error;
}
}

async signAndSubmitTransaction(
transaction: TransactionPayload,
options?: any
): Promise<{ hash: Types.HexEncodedBytes }> {
try {
const response = await this.provider?.signAndSubmitTransaction(
transaction,
options
);
if ((response as AptosWalletErrorResult).code) {
throw new Error((response as AptosWalletErrorResult).message);
}
return response as { hash: Types.HexEncodedBytes };
} catch (error: any) {
const errMsg = error.message;
throw errMsg;
}
}

async signMessage(message: SignMessagePayload): Promise<SignMessageResponse> {
try {
if (typeof message !== "object" || !message.nonce) {
throw `${AptosWalletName} Invalid signMessage Payload`;
}
const response = await this.provider?.signMessage(message);
if (response) {
return response;
} else {
throw `${AptosWalletName} Sign Message failed`;
}
} catch (error: any) {
const errMsg = error.message;
throw errMsg;
}
}

async onNetworkChange(callback: any): Promise<void> {
try {
const handleNetworkChange = async (newNetwork: {
networkName: NetworkInfo;
}): Promise<void> => {
callback({
name: newNetwork.networkName,
chainId: undefined,
api: undefined,
});
};
await this.provider?.onNetworkChange(handleNetworkChange);
} catch (error: any) {
const errMsg = error.message;
throw errMsg;
}
}

async onAccountChange(callback: any): Promise<void> {
try {
const handleAccountChange = async (
newAccount: AccountInfo
): Promise<void> => {
if (newAccount?.publicKey) {
callback({
publicKey: newAccount.publicKey,
address: newAccount.address,
});
} else {
const response = await this.connect();
callback({
address: response?.address,
publicKey: response?.publicKey,
});
}
};
await this.provider?.onAccountChange(handleAccountChange);
} catch (error: any) {
console.log(error);
const errMsg = error.message;
throw errMsg;
}
}

async network(): Promise<NetworkInfo> {
try {
const response = await this.provider?.network();
if (!response) throw `${AptosWalletName} Network Error`;
return {
name: response as NetworkName,
};
} catch (error: any) {
throw error;
}
}
}
9 changes: 9 additions & 0 deletions packages/aptos-wallet-plugin-example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@aptos-labs/wallet-adapter-tsconfig/base.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"target": "es5",
"lib": ["ES2015", "dom", "es2017", "ES2019.Array"]
}
}
7 changes: 4 additions & 3 deletions packages/wallet-adapter-core/src/WalletCore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HexString, Types } from "aptos";
import { HexString } from "aptos";
import EventEmitter from "eventemitter3";
import nacl from "tweetnacl";
import { Buffer } from "buffer";
Expand Down Expand Up @@ -29,6 +29,7 @@ import {
Wallet,
WalletInfo,
WalletCoreEvents,
TransactionPayload,
} from "./types";
import {
removeLocalStorage,
Expand Down Expand Up @@ -239,7 +240,7 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
@throws WalletSignAndSubmitMessageError
*/
async signAndSubmitTransaction(
transaction: Types.TransactionPayload
transaction: TransactionPayload
): Promise<any> {
try {
this.doesWalletExist();
Expand All @@ -261,7 +262,7 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
@throws WalletSignTransactionError
*/
async signTransaction(
transaction: Types.TransactionPayload
transaction: TransactionPayload
): Promise<Uint8Array | null> {
if (this._wallet && !("signTransaction" in this._wallet)) {
throw new WalletNotSupportedMethod(
Expand Down
9 changes: 6 additions & 3 deletions packages/wallet-adapter-core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Types } from "aptos";
import { TxnBuilderTypes, Types } from "aptos";
import { NetworkName, WalletReadyState } from "./constants";

// WalletName is a nominal type that wallet adapters should use, e.g. `'MyCryptoWallet' as WalletName<'MyCryptoWallet'>`
Expand All @@ -23,7 +23,6 @@ export interface AptosWalletErrorResult {
name: string;
message: string;
}

export interface PluginProvider {
connect: () => Promise<AccountInfo>;
account: () => Promise<AccountInfo>;
Expand All @@ -47,6 +46,10 @@ export interface AdapterPluginEvents {
onAccountChange(callback: any): Promise<any>;
}

export type TransactionPayload =
| Types.TransactionPayload
| TxnBuilderTypes.TransactionPayload;

export interface AdapterPluginProps<Name extends string = string> {
name: WalletName<Name>;
url: string;
Expand All @@ -56,7 +59,7 @@ export interface AdapterPluginProps<Name extends string = string> {
connect(): Promise<any>;
disconnect: () => Promise<any>;
network: () => Promise<any>;
signAndSubmitTransaction<T extends Types.TransactionPayload, V>(
signAndSubmitTransaction<T extends TransactionPayload, V>(
transaction: T,
options?: V
): Promise<{ hash: Types.HexEncodedBytes }>;
Expand Down
9 changes: 3 additions & 6 deletions packages/wallet-adapter-react/src/WalletProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import type {
AccountInfo,
NetworkInfo,
SignMessagePayload,
TransactionPayload,
Wallet,
WalletInfo,
WalletName,
} from "@aptos-labs/wallet-adapter-core";
import { WalletCore } from "@aptos-labs/wallet-adapter-core";

import { Types } from "aptos";

export interface AptosWalletProviderProps {
children: ReactNode;
plugins: Wallet[];
Expand Down Expand Up @@ -69,17 +68,15 @@ export const AptosWalletAdapterProvider: FC<AptosWalletProviderProps> = ({
}
};

const signAndSubmitTransaction = async (
transaction: Types.TransactionPayload
) => {
const signAndSubmitTransaction = async (transaction: TransactionPayload) => {
try {
return await walletCore.signAndSubmitTransaction(transaction);
} catch (error: any) {
throw error;
}
};

const signTransaction = async (transaction: Types.TransactionPayload) => {
const signTransaction = async (transaction: TransactionPayload) => {
try {
return await walletCore.signTransaction(transaction);
} catch (error: any) {
Expand Down
Loading

0 comments on commit 06a2e0d

Please sign in to comment.