Skip to content

Commit

Permalink
add sign to transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
zlayine committed Jul 28, 2023
1 parent 8e5028b commit c96d889
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 111 deletions.
62 changes: 62 additions & 0 deletions resources/js/components/SignTransaction.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<template>
<Btn primary @click="signTransaction()">
<LoadingCircle v-if="isLoading" class="h-5 w-5 mx-1 ml-0.5 text-white" />
<span v-else> Sign </span>
</Btn>
<Modal :is-open="showAccountsModal" :close="closeModal" width="max-w-lg">
<DialogTitle as="h3" class="text-lg font-medium leading-6 text-gray-900 text-center">
Select Account
</DialogTitle>
<div class="flex flex-col space-y-2 mt-4">
<div
v-for="account in useAppStore().accounts"
:key="account.address"
class="px-4 py-3 border border-gray-300 rounded-md text-sm cursor-pointer hover:bg-primary/20 transition-all"
@click="selectAccount(account)"
>
{{ publicKeyToAddress(account.address) }}
</div>
</div>
</Modal>
</template>

<script setup lang="ts">
import { DialogTitle } from '@headlessui/vue';
import Btn from './Btn.vue';
import Modal from './Modal.vue';
import { publicKeyToAddress } from '~/util/address';
import { useAppStore } from '~/store';
import { ref } from 'vue';
import LoadingCircle from './LoadingCircle.vue';
import snackbar from '~/util/snackbar';
const props = defineProps<{
transaction: any;
}>();
const isLoading = ref(false);
const showAccountsModal = ref(false);
const signTransaction = async () => {
useAppStore().getAccounts();
showAccountsModal.value = true;
};
const closeModal = () => {
showAccountsModal.value = false;
};
const selectAccount = async (account) => {
try {
isLoading.value = true;
useAppStore().setAccount(account);
showAccountsModal.value = false;
await useAppStore().signTransaction(props.transaction);
} catch (e) {
console.log(e)
snackbar.error({ title: 'Failed to sign transaction' });
} finally {
isLoading.value = false;
}
};
</script>
80 changes: 5 additions & 75 deletions resources/js/components/WalletConnectButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
</MenuItem>
</template>
<template v-else>
<div class="px-4 py-2 text-sm text-center">
<Address :address="walletSession.address" short />
</div>
<MenuItem v-slot="{ active }">
<button
:class="[
Expand All @@ -56,41 +53,24 @@
</MenuItems>
</ScaleTransition>
</Menu>
<Modal :is-open="showAccountsModal" :close="closeModal" width="max-w-lg">
<DialogTitle as="h3" class="text-lg font-medium leading-6 text-gray-900 text-center">
Select Account
</DialogTitle>
<div class="flex flex-col space-y-2 mt-4">
<div
v-for="account in appStore.accounts"
:key="account.address"
class="px-4 py-3 border border-gray-300 rounded-md text-sm cursor-pointer hover:bg-primary/20 transition-all"
@click="selectAccount(account)"
>
{{ publicKeyToAddress(account.address) }}
</div>
</div>
</Modal>

</template>

<script setup lang="ts">
import { WalletIcon } from '@heroicons/vue/24/outline';
import { useAppStore } from '~/store';
import LoadingCircle from './LoadingCircle.vue';
import { computed, ref } from 'vue';
import { DialogTitle, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue';
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue';
import ScaleTransition from './ScaleTransition.vue';
import snackbar from '~/util/snackbar';
import Modal from './Modal.vue';
import { publicKeyToAddress } from '~/util/address';
import Address from './Address.vue';
const appStore = useAppStore();
const loading = ref(true);
const showAccountsModal = ref(false);
const walletSession = computed(() => appStore.account);
const walletSession = computed(() => appStore.wallet);
const connectWallet = async (provider: string) => {
try {
Expand All @@ -110,7 +90,8 @@ const getSession = async () => {
try {
loading.value = true;
await appStore.getSession();
} catch {
} catch (e){
console.log(e)
snackbar.error({ title: 'Failed to get session' });
} finally {
loading.value = false;
Expand All @@ -123,57 +104,6 @@ const disconnectWallet = async () => {
loading.value = false;
};
const closeModal = () => {
showAccountsModal.value = false;
};
const selectAccount = (account) => {
// I don't think we should select an account as the person can use multiple accounts to sign
// The account can be determined by the "signingWallet" in the transaction.
// Unless we are not using that, then we would need a way to select an account indeed
appStore.setAccount(account);
showAccountsModal.value = false;
appStore.signTransaction();
};
// function makeTransaction() {
// // This is the call that comes from the platform transactions 'encodedCall'
// const call = '0a03008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48a10f';
// const era = '00'; // 00 is for immortal transactions
// const nonce = 'c8'; // Thats the account nonce
// const tip = '00'; // Tip should always be 00
// const spec = 'c60b0000'; // The spec version of runtime
// const txVersion = '08000000'; // The tx version of runtime
// const genesis = '99ded175d436bee7d751fa3f2f8c7a257ddc063a541f8daa5e6152604f66b2a0'; // The genesis block
// const blockHash = '99ded175d436bee7d751fa3f2f8c7a257ddc063a541f8daa5e6152604f66b2a0'; // For immortal transactions the blockhash needs to be the genesis
// const extra = era + nonce + tip; // This is the extra data that needs to be added in the final extrinsic and in the signing payload
// const addExtra = spec + txVersion + genesis + blockHash; // This is the extra data that needs to be added only in the signing payload
// const payload = call + extra + addExtra; // This is the payload to sign
// const bytes = hexToU8a(payload).byteLength;
// const size = compact.enc(bytes); // This is the compact version of the payload length
// Send the payload above to the wallet to sign
// The wallet will return a signature
// // To build the final extrinsic we need to do this
// // extrinsicVersion = 4; // Come from extrinsic.version in metadata
// // Signed transaction
// // extraByte = extrinsicVersion | 128;
// // extraByte = extraByte.toRadixString(16);
// // signerType = '00' // MultiAddress?
// // signatureType = '00';
// // 00 = ed25519
// // 01 = sr25519
// Error: Call to a member function increment() on null (Line 42 in /var/www/releases/76bbfc0db4eef154ef7054b0ab64ada66143fe43/vendor/enjin/platform-core/src/Services/Processor/Substrate/Events/Implementations/MultiTokens/Transferred.php)
// const finalExtrinsic =
// 'size of the extrinsic in compact' + extaByte + signerType + signer + signatureType + signature + extra + call;
// Send the above to the blockchain
// }
(async () => {
getSession();
Expand Down
13 changes: 13 additions & 0 deletions resources/js/components/pages/Transactions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
Result
</th>
<th
scope="col"
class="px-3 py-3.5 text-right text-sm font-semibold text-gray-900 truncate"
>
Sign
</th>
<th
scope="col"
class="px-3 py-3.5 text-right text-sm font-semibold text-gray-900 truncate"
Expand Down Expand Up @@ -109,6 +115,12 @@
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
<TransactionResultChip v-if="transaction.result" :text="transaction.result" />
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
<SignTransaction
v-if="transaction.state === 'PENDING'"
:transaction="transaction"
/>
</td>
<td
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-3 flex justify-end"
>
Expand Down Expand Up @@ -150,6 +162,7 @@ import TransactionResultChip from '~/components/TransactionResultChip.vue';
import { TransactionResultType, TransactionState, TransactionMethods } from '~/types/types.enums';
import NoItems from '~/components/NoItems.vue';
import snackbar from '~/util/snackbar';
import SignTransaction from '../SignTransaction.vue';
const isLoading = ref(false);
const isPaginationLoading = ref(false);
Expand Down
2 changes: 2 additions & 0 deletions resources/js/graphql/query/GetTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export default `query GetTransactions($idempotencyKeys: [String], $ids: [BigInt!
result
state
transactionId
encodedData
signedAtBlock
events {
edges {
node {
Expand Down
77 changes: 41 additions & 36 deletions resources/js/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import { wcOptions } from '~/util';
import { wcRequiredNamespaces } from '~/util';
import { getSdkError } from '@walletconnect/utils';
import { PolkadotjsWallet, Wallet } from '@talismn/connect-wallets';
import { addressToPublicKey } from '~/util/address';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { hexToU8a, stringToHex, u8aToHex } from '@polkadot/util';
import { compact } from 'scale-ts';
import { SignerPayloadJSON } from '@polkadot/types/types';
import { AccountInfoWithTripleRefCount } from '@polkadot/types/interfaces';

Expand Down Expand Up @@ -72,11 +68,12 @@ export const useAppStore = defineStore('app', {
newCollection: false,
user: null,
provider: '',
wallet: false,
account: null,
accounts: null,
}),
persist: {
paths: ['hostname', 'authorization_token', 'loggedIn', 'advanced', 'protocol', 'provider', 'account'],
paths: ['hostname', 'authorization_token', 'loggedIn', 'advanced', 'protocol', 'provider'],
},
actions: {
async init() {
Expand Down Expand Up @@ -261,8 +258,19 @@ export const useAppStore = defineStore('app', {
const walletConnect = this.getWeb3Modal();
const session = await walletConnect.getSession();
if (session && !session.acknowledged) {
this.account = null;
this.wallet = true;
}

return session;
} else if (this.provider === 'polkadot.js') {
const pkjs = new PolkadotjsWallet();

if (pkjs.installed) {
await pkjs.enable('Platform');
this.wallet = true;
}

return pkjs;
}
},
async connectWallet(provider) {
Expand All @@ -281,17 +289,9 @@ export const useAppStore = defineStore('app', {
requiredNamespaces: wcRequiredNamespaces,
});

if (session.acknowledged) {
const accounts = Object.values(session.namespaces)
.map((namespace) => namespace.accounts)
.flat()
.map((account) => {
return {
address: account.split(':')[2],
};
});
this.accounts = accounts;
if (session && session.acknowledged) {
this.provider = 'wc';
this.wallet = true;

return;
}
Expand All @@ -307,33 +307,29 @@ export const useAppStore = defineStore('app', {
const pkjs = new PolkadotjsWallet();
if (pkjs.installed) {
await pkjs.enable('Platform');
const accounts = await pkjs.getAccounts();
this.accounts = accounts;
this.provider = 'polkadot.js';
}
},
async signTransaction() {
async signTransaction(transaction: any) {
const provider = new WsProvider('wss://rpc.efinity.io');
const api = await ApiPromise.create({ provider });
const [genesisHash, runtime, account] = await Promise.all([
api.rpc.chain.getBlockHash(0),
api.rpc.state.getRuntimeVersion(),
<AccountInfoWithTripleRefCount>(
api.query.system.account('5D278Qv6qRviREhErNFAcxRkPmqx4mbNgqz7bq89osJpXUdP')
),
// @ts-ignore
<AccountInfoWithTripleRefCount>api.query.system.account(this.account.address),
]);
console.info(account);

// This is the call that comes from the platform transactions 'encodedCall'
const call = '0a03008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48a10f';
const call = transaction.encodedData;
const era = '00'; // 00 is for immortal transactions
const genesis = genesisHash.toHex(); // The genesis block
const blockHash = genesisHash.toHex(); // For immortal transactions the blockhash needs to be the genesis

const payloadToSign: SignerPayloadJSON = {
specVersion: runtime.specVersion.toString(),
transactionVersion: runtime.transactionVersion.toString(),
address: '5D278Qv6qRviREhErNFAcxRkPmqx4mbNgqz7bq89osJpXUdP',
address: this.account.address,
blockHash: blockHash,
blockNumber: '0x00',
era: '0x' + era,
Expand All @@ -345,25 +341,16 @@ export const useAppStore = defineStore('app', {
version: 4,
};

console.log(payloadToSign);
const { signature } = await this.account.signer.signPayload(payloadToSign);
console.log(signature);

const extrinsic = api.registry.createType(
'Extrinsic',
{ method: payloadToSign.method },
{ version: payloadToSign.version }
);
extrinsic.addSignature('5D278Qv6qRviREhErNFAcxRkPmqx4mbNgqz7bq89osJpXUdP', signature, payloadToSign);
console.log(extrinsic.toHex());
extrinsic.addSignature(this.account.address, signature, payloadToSign);

await this.connectToAPI(extrinsic.toHex() as any);
},
async connectToAPI(extrinsic: string) {
const provider = new WsProvider('wss://rpc.efinity.io');
const api = await ApiPromise.create({ provider });

const submit = await api.rpc.author.submitExtrinsic(extrinsic);
const submit = await api.rpc.author.submitExtrinsic(extrinsic.toHex());
console.log(submit);
},
async disconnectWallet() {
Expand All @@ -390,6 +377,24 @@ export const useAppStore = defineStore('app', {
setAccount(account: Wallet) {
this.account = account;
},
async getAccounts() {
if (this.provider === 'wc') {
const session = (await this.getSession()) as any;
const accounts = Object.values(session.namespaces)
.map((namespace: any) => namespace.accounts)
.flat()
.map((account) => {
return {
address: account.split(':')[2],
};
});
this.accounts = accounts;
} else if (this.provider === 'polkadot.js') {
const session = (await this.getSession()) as any;
const accounts = await session.getAccounts();
this.accounts = accounts;
}
},
},
getters: {
hasValidConfig(state: AppState) {
Expand Down
1 change: 1 addition & 0 deletions resources/js/types/types.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface AppState {
newCollection: boolean;
user: any;
provider: string;
wallet: boolean;
account: any;
accounts: any;
}
Expand Down

0 comments on commit c96d889

Please sign in to comment.