diff --git a/packages/extension/src/providers/kadena/libs/blockies.ts b/packages/extension/src/providers/kadena/libs/blockies.ts index 953475487..1540d5751 100644 --- a/packages/extension/src/providers/kadena/libs/blockies.ts +++ b/packages/extension/src/providers/kadena/libs/blockies.ts @@ -97,11 +97,15 @@ type options = { spotcolor?: string; }; +const formatAddress = (address: string): string => { + return address.replace("k:", "0x").toLowerCase(); +}; + const createIcon = (address: string, opts?: options): string => { opts = opts || {}; const size = opts.size || 8; const scale = opts.scale || 4; - const seed = address.toLowerCase(); + const seed = formatAddress(address); seedrand(seed); const color = opts.color || createColor(); const bgcolor = opts.bgcolor || createColor(); diff --git a/packages/extension/src/providers/kadena/types/kadena-network.ts b/packages/extension/src/providers/kadena/types/kadena-network.ts index 45d9ba644..cabdf4eb9 100644 --- a/packages/extension/src/providers/kadena/types/kadena-network.ts +++ b/packages/extension/src/providers/kadena/types/kadena-network.ts @@ -56,11 +56,12 @@ export class KadenaNetwork extends BaseNetwork { }; const baseOptions: BaseNetworkOptions = { - basePath: "m/44'/626'/0'", + basePath: "m/44'/626'/0'/0'", identicon: createIcon, signer: [SignerType.ed25519kda], provider: ProviderName.kadena, api, + importAccount: false, ...options, }; diff --git a/packages/extension/src/providers/kadena/ui/send-transaction/components/send-address-input.vue b/packages/extension/src/providers/kadena/ui/send-transaction/components/send-address-input.vue index 6e0a48df6..363727d83 100644 --- a/packages/extension/src/providers/kadena/ui/send-transaction/components/send-address-input.vue +++ b/packages/extension/src/providers/kadena/ui/send-transaction/components/send-address-input.vue @@ -84,7 +84,7 @@ const address = computed({ }); const isAddress = computed(() => { - return true; + return !!props.value; }); const changeFocus = (val: FocusEvent) => { diff --git a/packages/extension/src/providers/kadena/ui/send-transaction/index.vue b/packages/extension/src/providers/kadena/ui/send-transaction/index.vue index 4684f01a7..5bb6495c3 100644 --- a/packages/extension/src/providers/kadena/ui/send-transaction/index.vue +++ b/packages/extension/src/providers/kadena/ui/send-transaction/index.vue @@ -157,6 +157,7 @@ const selectedAsset = ref>( ); const hasEnough = ref(false); const sendMax = ref(false); +const addressToIsValid = ref(false); const selected: string = route.params.id as string; const isLoadingAssets = ref(true); @@ -217,6 +218,11 @@ const validateFields = async () => { ) ); + if (rawAmount.lten(0)) { + hasEnough.value = false; + return; + } + const localTransaction = await selectedAsset.value.buildTransaction!( addressTo.value, props.accountInfo.selectedAccount, @@ -229,14 +235,21 @@ const validateFields = async () => { localTransaction ); - const partialFee = transactionResult.gas; - const rawFee = toBN(partialFee?.toString() ?? "0"); + const gasLimit = transactionResult.metaData?.publicMeta?.gasLimit; + const gasPrice = transactionResult.metaData?.publicMeta?.gasPrice; + const gasFee = gasLimit && gasPrice ? gasLimit * gasPrice : 0; + + const rawFee = toBN( + toBase(gasFee.toString(), selectedAsset.value.decimals!) + ); const rawBalance = toBN(selectedAsset.value.balance!); + if ( sendMax.value && selectedAsset.value.name === accountAssets.value[0].name ) { - rawAmount = rawAmount.sub(rawFee); + rawAmount = rawBalance.sub(rawFee); + if (rawAmount.gtn(0)) { amount.value = fromBase( rawAmount.toString(), @@ -244,7 +257,8 @@ const validateFields = async () => { ); } } - if (rawAmount.ltn(0) || rawAmount.add(rawFee).gt(rawBalance)) { + + if (rawAmount.add(rawFee).gt(rawBalance)) { hasEnough.value = false; } else { hasEnough.value = true; @@ -252,7 +266,7 @@ const validateFields = async () => { const nativeAsset = accountAssets.value[0]; const txFeeHuman = fromBase( - partialFee?.toString() ?? "", + rawFee?.toString() ?? "", nativeAsset.decimals! ); @@ -264,6 +278,22 @@ const validateFields = async () => { nativeSymbol: nativeAsset.symbol ?? "", nativeValue: txFeeHuman.toString(), }; + + const to = props.network.displayAddress(addressTo.value); + + if (to.startsWith("k:") && to.length == 66) { + addressToIsValid.value = true; + } else { + const accountDetail = await accountAssets.value[0].getAccountDetails( + to, + props.network + ); + if (accountDetail.error) { + addressToIsValid.value = false; + } else { + addressToIsValid.value = true; + } + } } }; watch([selectedAsset, addressTo], validateFields); @@ -401,6 +431,7 @@ const isDisabled = computed(() => { amount.value !== "" && hasEnough.value && addressIsValid && + addressToIsValid.value && !edWarn.value && edWarn.value !== undefined ) diff --git a/packages/extension/src/types/base-network.ts b/packages/extension/src/types/base-network.ts index dfcc2efe3..fc9ad4cc8 100644 --- a/packages/extension/src/types/base-network.ts +++ b/packages/extension/src/types/base-network.ts @@ -33,6 +33,7 @@ export interface BaseNetworkOptions { | Promise | Promise; customTokens?: boolean; + importAccount?: boolean; } export abstract class BaseNetwork { @@ -60,6 +61,7 @@ export abstract class BaseNetwork { | Promise | Promise; public customTokens: boolean; + public importAccount: boolean; constructor(options: BaseNetworkOptions) { this.name = options.name; @@ -80,6 +82,7 @@ export abstract class BaseNetwork { this.decimals = options.decimals; this.api = options.api; this.customTokens = options.customTokens ?? false; + this.importAccount = options.importAccount ?? true; this.coingeckoPlatform = options.coingeckoPlatform; this.currencyNameLong = options.currencyNameLong; } diff --git a/packages/extension/src/ui/action/views/accounts/index.vue b/packages/extension/src/ui/action/views/accounts/index.vue index bc782856f..0e045ed95 100644 --- a/packages/extension/src/ui/action/views/accounts/index.vue +++ b/packages/extension/src/ui/action/views/accounts/index.vue @@ -60,7 +60,11 @@ Add hardware wallet account - + Import account from another wallet @@ -91,7 +95,7 @@ /> { const keyring = new KeyRing(storage); await keyring.init(password, { mnemonic: MNEMONIC }); const keyAdd: KeyRecordAdd = { - basePath: "m/44'/626'/0'", + basePath: "m/44'/626'/0'/0'/0'", signerType: SignerType.ed25519kda, name: "0index", walletType: WalletType.mnemonic, @@ -26,7 +26,7 @@ describe("Keyring create tests", () => { expect(pair.signerType).equals(SignerType.ed25519kda); expect(pair.pathIndex).equals(0); expect(pair.address).equals( - "0x3379098c10716e2ba981a65129e0e7c4b7f11d944412d3ab1001f49114f9d24d" + "0x7359492db65e4e6487134cd68d5620e011965ce50c84f38e613c7bdb47c2bfa3" ); keyring.lock(); }).timeout(20000); diff --git a/packages/signers/kadena/src/index.ts b/packages/signers/kadena/src/index.ts index f0ceb0db4..cc101b4ab 100644 --- a/packages/signers/kadena/src/index.ts +++ b/packages/signers/kadena/src/index.ts @@ -1,19 +1,15 @@ -import { SignerInterface, KeyPair } from "@enkryptcom/types"; +import { KeyPair, SignerInterface } from "@enkryptcom/types"; +import { bufferToHex, hexToBuffer } from "@enkryptcom/utils"; import { mnemonicToSeedSync } from "bip39"; import { sign as tweetSign } from "tweetnacl"; -import { bufferToHex, hexToBuffer } from "@enkryptcom/utils"; import { derivePath } from "./libs/ed25519"; class Signer implements SignerInterface { async generate(mnemonic: string, derivationPath = ""): Promise { const seed = bufferToHex(mnemonicToSeedSync(mnemonic), true); - const dPathSegments = derivationPath.split("/"); - const indexVal = Number(dPathSegments.pop()); - const keys = derivePath( - dPathSegments.join("/"), - seed, - 0x80000000 + indexVal - ); + const dPathSegments = `${derivationPath}'`.split("/"); + + const keys = derivePath(dPathSegments.join("/"), seed); const keyPair = tweetSign.keyPair.fromSeed(keys.key); return { address: bufferToHex(keyPair.publicKey), diff --git a/packages/signers/kadena/tests/generate.test.ts b/packages/signers/kadena/tests/generate.test.ts index 0402dc199..62ddbbb72 100644 --- a/packages/signers/kadena/tests/generate.test.ts +++ b/packages/signers/kadena/tests/generate.test.ts @@ -3,34 +3,34 @@ import Signer from "../src"; describe("Kadena address generate", () => { const MNEMONIC = - "clip coffee brain token leader kiss around main finger network avoid west"; + "favorite service senior cluster chicken shift square endorse casual kidney doll exhibit"; it("should generate Kadena addresses correctly", async () => { // Arrange const kadenaSigner = new Signer(); // Act & Assert - let keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0"); + let keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/0"); expect(keypair.address).equals( - "0xe84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291" + "0x40a9305bd53a921c44cf19dc9bac4e5d73465fc6a46343ab313defe6b0bfb0a3" ); // Act & Assert - keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/1"); + keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/1"); expect(keypair.address).equals( - "0x2ddd388820dfd8ddafa37a69926e0b5e57d29daa3adab2ede8a390f984038283" + "0x50d824cb62578b1fcf8e4afb122e98884d5f04070950b93a102e1ba1e1f3d1bb" ); // Act & Assert - keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/2"); + keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/2"); expect(keypair.address).equals( - "0x16dcf13ed0261406c63561ef32853e4b417fe1c197a164b6385718c596261918" + "0x287b9cdbd0894fbff67c664b4cc1e7da9eca9b03ef94fd5baa2cfabe2cd3c6a5" ); // Act & Assert - keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/3"); + keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/3"); expect(keypair.address).equals( - "0xaf29ee6f585381b0109eef8ba51f55fbe9cca31c83375efdb8d72588be3995fc" + "0xe00a31c57aabe95554ad71c700c723153c0dd67b072b569916209138e419c122" ); }); }); diff --git a/packages/signers/kadena/tests/sign.test.ts b/packages/signers/kadena/tests/sign.test.ts index 0567fe8c6..ddf8920e4 100644 --- a/packages/signers/kadena/tests/sign.test.ts +++ b/packages/signers/kadena/tests/sign.test.ts @@ -11,17 +11,17 @@ describe("Kadena signing", () => { "Everything should be made as simple as possible, but not simpler."; const msgHash = bufferToHex(blake2AsU8a(msg)); const signature = - "0xed96b2e3e21e021f3b3e0b39b93585705dbbc53a9cf940365f2ea61f71bdd8a68a3272bfc6e79d5f5b89cc32d85a9aba01ce04173038ede70c8d8da8f7cb4506"; + "0x8fd01eccc203d17cbffe54393954c213fb087dd6e62cf3c50bc5635346a83d9fae80c214ecb20bab092a0eca10408223e6e1007f597a3d4bfb525d68a0573a05"; const txMsg = '{"payload":{"exec":{"code":"(coin.transfer-create \\"k:e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291\\" \\"k:e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291\\" (read-keyset \\"ks\\") 0.000000000000)","data":{"ks":{"keys":["e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291"],"pred":"keys-all"}}}},"nonce":"kjs:nonce:1696630965601","signers":[{"pubKey":"e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291","scheme":"ED25519","clist":[{"name":"coin.TRANSFER","args":["k:e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291","k:e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291",{"decimal":"0"}]},{"name":"coin.GAS","args":[]}]}],"meta":{"gasLimit":2500,"gasPrice":1e-8,"sender":"k:e84affbb41a62d74020bc4841ea206aba7734f9e0d30fb688a4a84fe2d30e291","ttl":28800,"creationTime":1696630965,"chainId":"1"},"networkId":"testnet04"}'; const txMsgSig = - "0x8ee1c3cd94602b96a8ee59488c7ee7b0ff464b169513bd42872423219fa291c093bd791f62a11dd7aa00dfd3fa8e4b613e364ea9e4de4afe3d84ce3556673102"; + "0xe929eecf16d77016646a95448fd24de3183488a5e4ab7ae0b1fcb5971fd6e3a524a6cc2879b2369e40f827b816355dbaad09b173442742c39ba487c68199a302"; it("it should sign correctly", async () => { // Arrange const kadenaSigner = new Signer(); - const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/1"); + const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/1"); // Act const signResult = await kadenaSigner.sign(msgHash, keypair); @@ -33,7 +33,7 @@ describe("Kadena signing", () => { it("it should sign tx msgs correctly", async () => { // Arrange const kadenaSigner = new Signer(); - const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0"); + const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/0"); const txMsgHash = bufferToHex(blake2AsU8a(txMsg)); // Act const signResult = await kadenaSigner.sign(txMsgHash, keypair); @@ -44,7 +44,7 @@ describe("Kadena signing", () => { it("it should verify correctly", async () => { // Arrange const kadenaSigner = new Signer(); - const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/1"); + const keypair = await kadenaSigner.generate(MNEMONIC, "m/44'/626'/0'/0'/1"); // Act const verifyResult = await kadenaSigner.verify(