Giter VIP home page Giter VIP logo

evmosjs's Introduction

evmosjs

โš ๏ธ This librairy is no longer maintained. Many of the features are no longer working due to changes in the Evmos protocol that were not mirrored in this repository.

Also we encourage builders to interact with the protocol using EVM Extensions instead of building complex Cosmos transactions. EVM Extensions are basically precompiled stateful smart contracts. You can interact with them the same way you interact with EVM Solidity smart contracts.

For examples on how to integrate with Keplr, Metamask or how to process cosmos or evm transactions we invite you to checkout this repository as a reference: https://github.com/evmos/apps

Installation

evmosjs uses buf.build to manage Evmos Protobuf dependencies. To install evmosjs packages in your project, follow the instructions corresponding to your package manager.

NPM

Add the following line to an .npmrc file in your project root:

@buf:registry=https://buf.build/gen/npm/v1

Then run:

npm install evmosjs

Or:

npm install @evmos/<package>

Yarn v2.x or v3.x

Add the following to an .yarnrc.yml file in your project root:

npmScopes:
  buf:
    npmRegistryServer: "https://buf.build/gen/npm/v1"

Then run:

yarn add evmosjs

Or:

yarn add @evmos/<package>

Note that Yarn v1 is not supported (see explanation).

Usage and Examples

Query an Account

Query the account number, sequence, and pubkey for a given address.

import { generateEndpointAccount } from '@evmos/provider'

const address = 'evmos1...'

// Find node urls for either mainnet or testnet here:
// https://docs.evmos.org/develop/api/networks.
const nodeUrl = '...'
const queryEndpoint = `${nodeUrl}${generateEndpointAccount(address)}`

const restOptions = {
  method: 'GET',
  headers: { 'Content-Type': 'application/json' },
}

// Note that the node will return a 400 status code if the account does not exist.
const rawResult = await fetch(
  queryEndpoint,
  restOptions,
)

const result = await rawResult.json()

// The response format is available at @evmos/provider/rest/account/AccountResponse.
// Note that the `pub_key` will be `null` if the address has not sent any transactions.
/*
  account: {
    '@type': string
    base_account: {
      address: string
      pub_key?: {
        '@type': string
        key: string
      }
      account_number: string
      sequence: string
    }
    code_hash: string
  }
*/

Get an Account's Public Key

Use Keplr or MetaMask to retrieve an account's public key if it is not returned in the query response. The public key is necessary in order to sign and broadcast transactions, and it must be encoded as a compressed key in base64.

Keplr

const cosmosChainID = 'evmos_9001-2' // Use 'evmos_9000-4' for testnet

const account = await window?.keplr?.getKey(cosmosChainID)
const pk = Buffer.from(account.pubKey).toString('base64')

MetaMask

Since MetaMask does not provide an interface to retrieve a user's public key, we must sign a message and recover the key from a signature.

import { hashMessage } from '@ethersproject/hash'
import {
  computePublicKey,
  recoverPublicKey,
} from '@ethersproject/signing-key'

const accounts = await window?.ethereum?.request({
  method: 'eth_requestAccounts',
})

// Handle errors if MetaMask fails to return any accounts.
const message = 'Verify Public Key'

const signature = await window?.ethereum?.request({
  method: 'personal_sign',
  params: [message, accounts[0], ''],
})

// Compress the key, since the client expects
// public keys to be compressed.
const uncompressedPk = recoverPublicKey(
  hashMessage(message),
  signature,
)

const hexPk = computePublicKey(uncompressedPk, true)
const pk = Buffer.from(
  hexPk.replace('0x', ''), 'hex',
).toString('base64')

Create a Signable Transaction

Create a transaction payload which can be signed using either Metamask or Keplr.

This example uses MsgSend. View all signable transaction payloads in the Transaction Docs.

import {
  Chain,
  Sender,
  Fee,
  TxContext,
  MsgSendParams,
  createTxMsgSend,
  TxPayload,
} from '@evmos/transactions'

const chain: Chain = {
  chainId: 9001,
  cosmosChainId: 'evmos_9001-2',
}

// Populate the transaction sender parameters using the
// query API.
const sender: Sender = {
  accountAddress: <sender_account_address>,
  sequence: <sender_sequence>,
  accountNumber: <sender_account_number>,
  // Use the public key from the account query, or retrieve
  // the public key from the code snippet above.
  pubkey: <sender_pub_key>,
}

const fee: Fee = {
  amount: '4000000000000000',
  denom: 'aevmos',
  gas: '200000',
}

const memo = ''

const context: TxContext = {
  chain,
  sender,
  fee,
  memo,
}

const params: MsgSendParams = {
  destinationAddress: <destination_address>,
  amount: <transaction_amount>,
  denom: 'aevmos',
}

const tx: TxPayload = createTxMsgSend(context, params)

Sign the Transaction with MetaMask

Evmos supports EIP-712 signatures for Cosmos payloads to be signed using Ethereum wallets such as MetaMask.

import { createTxRaw } from '@evmos/proto'
import { evmosToEth } from '@evmos/address-converter'

// First, populate a TxContext object and create a signable Tx payload.
// (See 'Create a Signable Transaction' to learn how to create these).
const context = ...
const tx = ...

const { sender } = context

// Initialize MetaMask and sign the EIP-712 payload.
await window.ethereum.enable()

const senderHexAddress = evmosToEth(sender.accountAddress)
const eip712Payload = JSON.stringify(tx.eipToSign)

const signature = await window.ethereum.request({
  method: 'eth_signTypedData_v4',
  params: [senderHexAddress, eip712Payload],
})

// Create a signed Tx payload that can be broadcast to a node.
const signatureBytes = Buffer.from(signature.replace('0x', ''), 'hex')

const { signDirect } = tx
const bodyBytes = signDirect.body.toBinary()
const authInfoBytes = signDirect.authInfo.toBinary()

const signedTx = createTxRaw(
  bodyBytes,
  authInfoBytes,
  [signatureBytes],
)

Sign the Transaction with Keplr (SignDirect)

EvmosJS supports Cosmos SDK SignDirect payloads that can be signed using Keplr.

import { createTxRaw } from '@evmos/proto'

// First, populate a TxContext object and create a signable Tx payload.
// (See 'Create a Signable Transaction' to learn how to create these).
const context = ...
const tx = ...

const { chain, sender } = context
const { signDirect } = tx

const signResponse = await window?.keplr?.signDirect(
  chain.cosmosChainId,
  sender.accountAddress,
  {
    bodyBytes: signDirect.body.toBinary(),
    authInfoBytes: signDirect.authInfo.toBinary(),
    chainId: chain.cosmosChainId,
    accountNumber: new Long(sender.accountNumber),
  },
)

if (!signResponse) {
  // Handle signature failure here.
}

const signatures = [
  new Uint8Array(Buffer.from(signResponse.signature.signature, 'base64')),
]

const { signed } = signResponse

const signedTx = createTxRaw(
  signed.bodyBytes,
  signed.authInfoBytes,
  signatures,
)

Sign the Transaction with Keplr (EIP-712)

EvmosJS also supports signing EIP-712 payloads using Keplr. This is necessary for Ledger users on Keplr, since the Ledger device cannot sign SignDirect payloads.

import { EthSignType } from '@keplr-wallet/types';
import { createTxRaw } from '@evmos/proto'

// First, populate a TxContext object and create a signable Tx payload.
// (See 'Create a Signable Transaction' to learn how to create these).
const context = ...
const tx = ...

const { chain, sender } = context

const eip712Payload = JSON.stringify(tx.eipToSign)
const signature = await window?.keplr?.signEthereum(
  chain.cosmosChainId,
  sender.accountAddress,
  eip712Payload,
  EthSignType.EIP712,
)

if (!signature) {
  // Handle signature failure here.
}

const { signDirect } = tx
const bodyBytes = signDirect.body.toBinary()
const authInfoBytes = signDirect.authInfo.toBinary()

const signedTx = createTxRaw(
  bodyBytes,
  authInfoBytes,
  [signature],
)

Broadcast the Signed Transaction

Regardless of how the transaction is signed, broadcasting takes place the same way.

import {
  generateEndpointBroadcast,
  generatePostBodyBroadcast,
} from '@evmos/provider'

// First, sign a transaction using MetaMask or Keplr.
const signedTx = createTxRaw(...)

// Find a node URL from a network endpoint:
// https://docs.evmos.org/develop/api/networks.
const nodeUrl = ...

const postOptions = {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: generatePostBodyBroadcast(signedTx),
}

const broadcastEndpoint = `${nodeUrl}${generateEndpointBroadcast()}`
const broadcastPost = await fetch(
  broadcastEndpoint,
  postOptions,
)

const response = await broadcastPost.json()

evmosjs's People

Contributors

0a1c avatar dependabot[bot] avatar fedekunze avatar gatom22 avatar hanchon avatar ivanshukhov avatar johnletey avatar julia-script avatar malteherrmann avatar milipaoletti avatar olegshilov avatar omahs avatar sandoche avatar vvaradinov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

evmosjs's Issues

Is there any other way to sign message except with Keplr or Metamask?

Is there any other way to sign message except with Keplr or Metamask?

I'm trying to make the transaction with NodeJS and I'm lost on how to do it. I tried using cosmJS to do it but I can't because I get this error: Error: Unsupported type: '/ethermint.types.v1.EthAccount' is there some way to send a tranasction without web interface?

Send a Tx with createTxMsgConvertERC20 through Keplr

Hello, i tried to send a conversion Tx:

  await window.keplr.enable("evmos_9001-2");
  const addr_keplr = await window.keplr.getKey("evmos_9001-2");
  const pub_key = Buffer.from(addr_keplr.pubKey).toString('base64')

  const fee = {
    denom:"aevmos",
    amount:"250000000000000000",
    gas: "2500000"
  }
  
  const chain = {
    chainId: 9000,
    cosmosChainId: "evmos_9001-2"
  }

  const sender = {
    accountAddress: this.address_keplr,
    sequence: 0,
    accountNumber: 0,
    pubkey: pub_key
  }

  const msgConvert = {
    contract_address: "0x205CF44075E77A3543abC690437F3b2819bc450a",
    amount: "10",
    receiverEvmosFormatted: this.address_keplr,
    senderHexFormatted: this.address_metamask
  }
  
  let msg = createTxMsgConvertERC20(chain,sender,fee,"",msgConvert);

  const signed_tx = await window.keplr.signDirect(
    "evmos_9001-2",
    this.address_keplr,
    {
      bodyBytes: msg.signDirect.body.serialize(),
      authInfoBytes: msg.signDirect.authInfo.serialize(),
      chainId: "evmos_9001-2",
      accountNumber: 0
    },
  );

But in the data section of Keplr, message content is protobuffed, not in plaintext (and the Tx is not sent):

 {
  "txBody": {
    "messages": [
      {
        "type_url": "/evmos.erc20.v1.MsgConvertERC20",
        "value": "CioweDIwNUNGNDQwNzVFNzdBMzU0M2FiQzY5MDQzN0YzYjI4MTliYzQ1MGESAjEwGixldm1vczF......"
      }
    ],
    "memo": "",
    "timeoutHeight": "0",
    "extensionOptions": [],
    "nonCriticalExtensionOptions": []
  },
  "authInfo": {
    "signerInfos": [
      {
        "publicKey": {
          "typeUrl": "/ethermint.crypto.v1.ethsecp256k1.PubKey",
          "value": "<BASE64>"
        },
        "modeInfo": {
          "single": {
            "mode": "SIGN_MODE_DIRECT"
          }
        },
        "sequence": "0"
      }
    ],
    "fee": {
      "amount": [
        {
          "denom": "aevmos",
          "amount": "62500000000000000"
        }
      ],
      "gasLimit": "2500000",
      "payer": "",
      "granter": ""
    }
  },
  "chainId": "evmos_9001-2",
  "accountNumber": "0"
}

Signature verification failed for EIP712 signed with metamask

I have some issues with signing with metamask.

I am currently facing some challenges while attempting to sign a transaction created with evmosjs using Metamask. Specifically, I am encountering an issue when trying to create and sign a delegate message with Metamask. Unfortunately, upon broadcasting the transaction, I received the following response:

{
  "height": "0",
  "txhash": "B1AB968A3616F5FA8E8F7BC831EFE61206291EAFA1AD7881F5F51854FE71AA81",
  "codespace": "sdk",
  "code": 4,
  "data": "",
  "raw_log": "signature verification failed; please verify account number (8) and chain-id (haqq_121799-1): unauthorized",
  "logs": [],
  "info": "",
  "gas_wanted": "0",
  "gas_used": "0",
  "tx": null,
  "timestamp": "",
  "events": []
}

I would greatly appreciate any assistance or guidance you can provide to help resolve this issue.
To further illustrate this compatibility issue, I have created a reproduction repository that demonstrates the problem. You can find it at the following link: https://github.com/olegshilov/evmosjs-metamask-sign-error

Can't import @evmos/transactions and @evmos/provider

Hey @hanchon,

Was trying out the Evmosjs within a React project - but I didn't get too far. Not too sure what the issue is. After installing npm i evmosjs, I imported the following:

import { createMessageSend } from '@evmos/transactions'
import { evmosToEth } from '@evmos/address-converter'
import { generateEndpointBroadcast, generatePostBodyBroadcast } from '@evmos/provider'
import { createTxRawEIP712, signatureToWeb3Extension } from '@evmos/transactions'

And the browser shows the following error messages:

Compiled with problems:X

ERROR in ./node_modules/@cosmjs/crypto/build/pbkdf2.js 46:67-84

Module not found: Error: Can't resolve 'crypto' in '/Users/lei/github_projects/evmos-wallet-integration/node_modules/@cosmjs/crypto/build'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
	- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "crypto": false }


ERROR in ./node_modules/cipher-base/index.js 2:16-43

Module not found: Error: Can't resolve 'stream' in '/Users/lei/github_projects/evmos-wallet-integration/node_modules/cipher-base'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
	- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "stream": false }


ERROR in ./node_modules/ethereumjs-util/dist/account.js 7:13-30

Module not found: Error: Can't resolve 'assert' in '/Users/lei/github_projects/evmos-wallet-integration/node_modules/ethereumjs-util/dist'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "assert": require.resolve("assert/") }'
	- install 'assert'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "assert": false }


ERROR in ./node_modules/ethereumjs-util/dist/object.js 7:13-30

Module not found: Error: Can't resolve 'assert' in '/Users/lei/github_projects/evmos-wallet-integration/node_modules/ethereumjs-util/dist'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "assert": require.resolve("assert/") }'
	- install 'assert'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "assert": false }


ERROR in ./node_modules/libsodium/dist/modules/libsodium.js 39:23-46

Module not found: Error: Can't resolve 'path' in '/Users/lei/github_projects/evmos-wallet-integration/node_modules/libsodium/dist/modules'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
	- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "path": false }

Im using node version 14.7.0, and react version 18.2.0.

Address converter doesn't convert ETH/EVMOS to Cosmos correctly

I'm testing your new/migrated "@evmos/address-converter" and I found that there are also ethToCosmos and cosmosToEth

Unfortunately, when converting EVM/Evmos address back to cosmos or vice versa, the result address is not the same as in Kepler wallet (and is empty).

Is there anything required to do to convert it correctly?

EIP712 signature is different from Evmos

Error:

signature verification failed; please verify account number (1) and chain-id (evmosd_9000-1): 
feePayer pubkey XXXXX is different from transaction pubkey YYYYY: 
invalid pubkey [ethermint/app/ante/eip712.go:253]: unauthorized

Evmos Output:

EIP712Domain
โ”‚ (name, string): 0xed511d1b60c53e1dda1a62cb1bc3a431dfb96636c1c0605811f47bb130590e06
โ”‚ (version, string): 0x06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c
โ”‚ (chainId, uint256): 0x0000000000000000000000000000000000000000000000000000000000002328
โ”‚ (verifyingContract, string): 0xcd0caab714c7e13c9cdac9c7ba1a5de819d5977baf81b2494d77c12ce653cf95
โ”‚ (salt, string): 0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d
EIP712Domain: 0x18a89ea35edd8815ce767aaabc09a316b4fbc08f3201680390a31f7da513f3ec
------------------------------------------------------------------------------------------------------------
Tx
โ”‚ (account_number, string): 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6
โ”‚ (chain_id, string): 0x4f94eae3b31c73300aa358b11ad83fb40dcf72060ec00242d5e41d7f0681bb5e
โ”‚ Fee
โ”‚ โ”‚ (feePayer, string): 0xd8af1d5ff9f1341ebd135924a98cfef27fc9dd201080708fbc93d54081c98075
โ”‚ โ”‚ Coin
โ”‚ โ”‚ โ”‚ (denom, string): 0x3412d8cf48701062ba2193424e8669d7c1571e513fe7eb7255d0bf6ecc609c55
โ”‚ โ”‚ โ”‚ (amount, string): 0x731dc163f73d31d8c68f9917ce4ff967753939f70432973c04fd2c2a48148607
โ”‚ โ”‚ Coin: 0xedb8bc2778e7252283da47cda5d29163ed422e6d91f7a389fe210c6d3dd0e284
โ”‚ โ”‚ (amount, Coin[]): 0x8fd6cc334116397ee8a138fd678e19b7e61264d78213abcf0ffd496d8711294a
โ”‚ โ”‚ (gas, string): 0x732a4ddce4090e7e9bbe43ada033fb7e5a6b1454f28bc8b89ecede170fa30e55
โ”‚ Fee: 0xd8a9b05927ce6842fd7fa0fb8159b3a5c024c5f2c7172bcb0aa1aa5ba8b7df85
โ”‚ (memo, string): 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
โ”‚ Msg
โ”‚ โ”‚ (type, string): 0x163cbcdd6e9b32a6103f55cb6a9ae0d1147994d786b376f876dd5c18b3b8cf60
โ”‚ โ”‚ MsgValue
โ”‚ โ”‚ โ”‚ TypeContent
โ”‚ โ”‚ โ”‚ โ”‚ (type, string): 0x067cd3b34e3c2c33024f43af49f69db2486ba2a6a10ce6a8f93c9a092425e65f
โ”‚ โ”‚ โ”‚ โ”‚ TypeContentValue
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (title, string): 0xd1b7a126ca51b5d1b8bf38748776ce284dd709ebe46dec0b3eb50fa22aca428d
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (description, string): 0xfd4585b83afaf41e69e197a5c5507e0a3cd33022b7a9d41d2c057a37a591496d
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (addresses, string[]): 0x441553465461e9544f5c966f4dc13e79709681d0e73f447dfec3fd11b55798fe
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (operation, int32): 0x0000000000000000000000000000000000000000000000000000000000000001
โ”‚ โ”‚ โ”‚ โ”‚ TypeContentValue: 0xf1c00916fb7bc4b0b2e1b9a512ac222a47a16617dbc9a525f97558bc909f7a7c
โ”‚ โ”‚ โ”‚ TypeContent: 0x61553768e44a38e1ea29f69fae0a79ace265df87c25c31ae61d3a63623e24d1f
โ”‚ โ”‚ โ”‚ TypeInitialDeposit
โ”‚ โ”‚ โ”‚ โ”‚ (denom, string): 0x3412d8cf48701062ba2193424e8669d7c1571e513fe7eb7255d0bf6ecc609c55
โ”‚ โ”‚ โ”‚ โ”‚ (amount, string): 0x204e643c29c1c107bde84b5d4b708d642847949ce9f47120daf41797d5515ceb
โ”‚ โ”‚ โ”‚ TypeInitialDeposit: 0xc5f49734e6bd206face7d6bc6da1875759fb63241ec99b5e37a77bf323f3333e
โ”‚ โ”‚ โ”‚ (initial_deposit, TypeInitialDeposit[]): 0x909f66e53f96ae5fbbc592a6a14576d9cdf47caf416b633bdbcdbfe6430c647a
โ”‚ โ”‚ โ”‚ (proposer, string): 0xd8af1d5ff9f1341ebd135924a98cfef27fc9dd201080708fbc93d54081c98075
โ”‚ โ”‚ MsgValue: 0xc326ec69f574c4fbba755f53be02ed8a18d8a03749a24aeea5d7764a14bd9ee4
โ”‚ Msg: 0xa30ad0ecc2f2ceabe473c8e114e749cffd365431d1f038e797c8d474f8e6c61a
โ”‚ (msgs, Msg[]): 0x31f4ab68bd6f215b9dbcdf04f149dc7a734f80027ceaf2dd1c91b02f886df8b0
โ”‚ (sequence, string): 0x1572b593c53d839d80004aa4b8c51211864104f06ace9e22be9c4365b50655ea
Tx: 0x13ce255ba37df8236d2603662ec8f3cf69577689d024a1ef31d90ad8d96c3fbb

Evmosjs Output:

{
  field_name: 'name',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'ed511d1b60c53e1dda1a62cb1bc3a431dfb96636c1c0605811f47bb130590e06'
}
{
  field_name: 'version',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c'
}
{
  field_name: 'chainId',
  field_type: 'uint256',
  ecnoded_type: 'uint256',
  encoded_value: 9000
}
{
  field_name: 'verifyingContract',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'cd0caab714c7e13c9cdac9c7ba1a5de819d5977baf81b2494d77c12ce653cf95'
}
{
  field_name: 'salt',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d'
}
{
  field_name: 'denom',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '3412d8cf48701062ba2193424e8669d7c1571e513fe7eb7255d0bf6ecc609c55'
}
{
  field_name: 'amount',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '731dc163f73d31d8c68f9917ce4ff967753939f70432973c04fd2c2a48148607'
}
{
  field_name: 'feePayer',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'd8af1d5ff9f1341ebd135924a98cfef27fc9dd201080708fbc93d54081c98075'
}
{
  field_name: 'amount',
  field_type: 'Coin[]',
  ecnoded_type: 'bytes32',
  encoded_value: '8fd6cc334116397ee8a138fd678e19b7e61264d78213abcf0ffd496d8711294a'
}
{
  field_name: 'gas',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '732a4ddce4090e7e9bbe43ada033fb7e5a6b1454f28bc8b89ecede170fa30e55'
}
{
  field_name: 'title',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'd1b7a126ca51b5d1b8bf38748776ce284dd709ebe46dec0b3eb50fa22aca428d'
}
{
  field_name: 'description',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'fd4585b83afaf41e69e197a5c5507e0a3cd33022b7a9d41d2c057a37a591496d'
}
{
  field_name: 'operation',
  field_type: 'int32',
  ecnoded_type: 'int32',
  encoded_value: 1
}
{
  field_name: 'addresses',
  field_type: 'string[]',
  ecnoded_type: 'bytes32',
  encoded_value: '441553465461e9544f5c966f4dc13e79709681d0e73f447dfec3fd11b55798fe'
}
{
  field_name: 'type',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '067cd3b34e3c2c33024f43af49f69db2486ba2a6a10ce6a8f93c9a092425e65f'
}
{
  field_name: 'value',
  field_type: 'TypeContentValue',
  ecnoded_type: 'bytes32',
  encoded_value: 'c53585fab8a18c73dd5e4b18cc49445589250f9cd364fd55c71db22d5fb384ec'
}
{
  field_name: 'denom',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '3412d8cf48701062ba2193424e8669d7c1571e513fe7eb7255d0bf6ecc609c55'
}
{
  field_name: 'amount',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '204e643c29c1c107bde84b5d4b708d642847949ce9f47120daf41797d5515ceb'
}
{
  field_name: 'content',
  field_type: 'TypeContent',
  ecnoded_type: 'bytes32',
  encoded_value: '425a0522be7459396c48141ec2a42d01aeba4f073796ee21317d50bd846cac52'
}
{
  field_name: 'initial_deposit',
  field_type: 'TypeInitialDeposit[]',
  ecnoded_type: 'bytes32',
  encoded_value: '909f66e53f96ae5fbbc592a6a14576d9cdf47caf416b633bdbcdbfe6430c647a'
}
{
  field_name: 'proposer',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'd8af1d5ff9f1341ebd135924a98cfef27fc9dd201080708fbc93d54081c98075'
}
{
  field_name: 'type',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '163cbcdd6e9b32a6103f55cb6a9ae0d1147994d786b376f876dd5c18b3b8cf60'
}
{
  field_name: 'value',
  field_type: 'MsgValue',
  ecnoded_type: 'bytes32',
  encoded_value: 'd22593d12079983ebf77502c5d4ff79c000c27af11452e9e8a8e40351c14713d'
}
{
  field_name: 'account_number',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6'
}
{
  field_name: 'chain_id',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '4f94eae3b31c73300aa358b11ad83fb40dcf72060ec00242d5e41d7f0681bb5e'
}
{
  field_name: 'fee',
  field_type: 'Fee',
  ecnoded_type: 'bytes32',
  encoded_value: 'd8a9b05927ce6842fd7fa0fb8159b3a5c024c5f2c7172bcb0aa1aa5ba8b7df85'
}
{
  field_name: 'memo',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
}
{
  field_name: 'msgs',
  field_type: 'Msg[]',
  ecnoded_type: 'bytes32',
  encoded_value: 'c567c282aedbb95689c0f5674aa01725bb75b044b9ca15dfba8533231759af63'
}
{
  field_name: 'sequence',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: '1572b593c53d839d80004aa4b8c51211864104f06ace9e22be9c4365b50655ea'
}
EIP712Domain: 18a89ea35edd8815ce767aaabc09a316b4fbc08f3201680390a31f7da513f3ec
Tx: b16aab0f5cab4aa0b288d9862c8fedf70a7a034b25bcc8cd65de0fbd8e8ab9d

It seems to be unable to handle TypeContentValue, correctly:

โ”‚ โ”‚ โ”‚ โ”‚ TypeContentValue
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (title, string): 0xd1b7a126ca51b5d1b8bf38748776ce284dd709ebe46dec0b3eb50fa22aca428d
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (description, string): 0xfd4585b83afaf41e69e197a5c5507e0a3cd33022b7a9d41d2c057a37a591496d
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (addresses, string[]): 0x441553465461e9544f5c966f4dc13e79709681d0e73f447dfec3fd11b55798fe
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (operation, int32): 0x0000000000000000000000000000000000000000000000000000000000000001
โ”‚ โ”‚ โ”‚ โ”‚ TypeContentValue: 0xf1c00916fb7bc4b0b2e1b9a512ac222a47a16617dbc9a525f97558bc909f7a7c
----------------------------------------------------------------------------------------------------------
{
  field_name: 'title',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'd1b7a126ca51b5d1b8bf38748776ce284dd709ebe46dec0b3eb50fa22aca428d'
}
{
  field_name: 'description',
  field_type: 'string',
  ecnoded_type: 'bytes32',
  encoded_value: 'fd4585b83afaf41e69e197a5c5507e0a3cd33022b7a9d41d2c057a37a591496d'
}
{
  field_name: 'operation',
  field_type: 'int32',
  ecnoded_type: 'int32',
  encoded_value: 1
}
{
  field_name: 'addresses',
  field_type: 'string[]',
  ecnoded_type: 'bytes32',
  encoded_value: '441553465461e9544f5c966f4dc13e79709681d0e73f447dfec3fd11b55798fe'
}
  • evmosjs version is 0.1.0
  • evmos version is v8.0.0

MsgSubmitProposal RLP Type

Anyone have any idea what the problem is with submitting a text-based governance proposal?
This is the only type of message for which I can't get Evmos + Ledger to work with Keplr.

I get the below error:

Error: signature verification failed; please verify account number (1990873) and chain-id (evmos_9001-2): feePayer pubkey EthPubKeySecp256k1{026F434A5E0356CDC54D9999145F5681628D042AE487DE0E44293C134DBBAE5CB6} is different from transaction pubkey EthPubKeySecp256k1{022E10BA94588FE6B3965F46F29F6EF68008B80EF823B0FFB746A53BCD61A3B915}: invalid pubkey [evmos/[email protected]/app/ante/eip712.go:253]: unauthorized
    at r (injectedScript.bundle.js:1:15627)

The used RLP Type:

{
    MsgValue: [
      { name: "content", type: "TypeProposalTextDescription" },
      { name: "initial_deposit", type: "TypeAmount[]" },
      { name: "proposer", type: "string" },
    ],
    TypeProposalTextDescription: [
      { name: "description", type: "string" },
      { name: "title", type: "string" },
      { name: "type", type: "string" },
    ],
    TypeAmount: [
      { name: "denom", type: "string" },
      { name: "amount", type: "string" }
    ],
}

The signDoc content looks like this:

{
    "chain_id": "evmos_9001-2",
    "account_number": "1990873",
    "sequence": "54",
    "fee": {
        "amount": [
            {
                "amount": "2500000000",
                "denom": "aevmos"
            }
        ],
        "gas": "250000",
        "feePayer": "evmos1lmdw0am9y4pa53n33rn8m9wgsyvhl8dhcllmm6"
    },
    "msgs": [
        {
            "type": "cosmos-sdk/MsgSubmitProposal",
            "value": {
                "content": {
                    "type": "cosmos-sdk/TextProposal",
                    "title": "test evmos+ledger with Keplr (please don't deposit for this proposal)",
                    "description": "test evmos+ledger with Keplr (please don't deposit for this proposal)"
                },
                "initial_deposit": [
                    {
                        "amount": "1000000000000",
                        "denom": "aevmos"
                    }
                ],
                "proposer": "evmos1lmdw0am9y4pa53n33rn8m9wgsyvhl8dhcllmm6"
            }
        }
    ],
    "memo": ""
}

If I use type: any for the content property, I get the below error:

Error: unknown type "any" (argument="types", value={"Tx":[{"name":"account_number","type":"string"},{"name":"chain_id","type":"string"},{"name":"fee","type":"Fee"},{"name":"memo","type":"string"},{"name":"msgs","type":"Msg[]"},{"name":"sequence","type":"string"}],"Fee":[{"name":"feePayer","type":"string"},{"name":"amount","type":"Coin[]"},{"name":"gas","type":"string"}],"Coin":[{"name":"denom","type":"string"},{"name":"amount","type":"string"}],"Msg":[{"name":"type","type":"string"},{"name":"value","type":"MsgValue"}],"MsgValue":[{"name":"content","type":"any"},{"name":"initial_deposit","type":"TypeAmount[]"},{"name":"proposer","type":"string"}],"TypeAmount":[{"name":"denom","type":"string"},{"name":"amount","type":"string"}]}, code=INVALID_ARGUMENT, version=hash/5.7.0)
    at r (injectedScript.bundle.js:formatted:847:41)

Thanks in advance ๐Ÿ™

Document example eip712 signed transaction with multiple messages

Hey there @hanchon, you suggested that you would help document an example here:

Trying to sign a tx via eip712 that contains multiple messages- this is roughly my implementation that's using eth_sign atm

  const delegatorAddress = ""
  const seq = await getSeq(chainId, delegatorAddress);

  const { address, sequence, accountNumber, pubkey } = seq;

  const sender = {
    accountAddress: address,
    sequence: sequence,
    accountNumber,
    pubkey: pubkey?.value,
  };

  const fee = {
    amount: (
      7_500_000_000_000_000
    ).toString(),
    denom: "aevmos",
    gas:  "375000"
  };
  
  /*
  const delegateMsgs = delegations.map((d) =>
    createMsgDelegate(
      delegatorAddress,
      d.validator,
      d.share.amount,
      d.share.denom
    )
  );
  */
  const delegateMsgs = [
    createMsgDelegate(
      delegatorAddress,
      "evmosvaloper1aafywn5c85v2m06wr9famd0p4wp8grt0pcsfsy",
      (1_000_000_000_000_000_000).toString(),
      "aevmos"
    ),
    createMsgDelegate(
      delegatorAddress,
      "evmosvaloper1w2j20fj2a4wfmvqnhfhv4mfxkw5xxt09x8esyj",
      (500_000_000_000_000_000).toString(),
      "aevmos"
    )
  ]  
  
  const rawTx = await signMetamaskTx({
    msgs: delegateMsgs,
    ethAddress: evmosToEth(delegatorAddress),
    mm,
    fee,
    sender,
    chainId,
  });

  if (rawTx) {
    return evmosBroadcast({ rawTx, chain: chainId });
  }
  
  
const signMetamaskTx = async ({
  msgs,
  fee,
  sender,
  ethAddress,
  mm,
  chainId,
  memo = "",
}: {
  mm: MetaMaskInpageProvider;
  ethAddress: string;
  msgs: {
    message: any;
    path: string;
  }[];
  fee: { amount: string; denom: string; gas: string };
  chainId: AvailableChain;
  sender: { pubkey: string; sequence: number; accountNumber: number };
  memo?: string;
}) => {
  const tx = createTransactionWithMultipleMessages(
    msgs,
    memo,
    fee.amount,
    fee.denom,
    parseInt(fee.gas, 10),
    "ethsecp256",
    sender.pubkey,
    sender.sequence,
    sender.accountNumber,
    chainId
  );

  // Request the signature
  let signature = await mm.request({
    method: "eth_sign",
    params: [
      ethAddress,
      "0x" + Buffer.from(tx.signDirect.signBytes, "base64").toString("hex"),
    ],
  });
  if (signature) {
    const signBytes = Buffer.from(signature.split("0x")[1], "hex");

    return createTxRaw(
      tx.signDirect.body.serializeBinary(),
      tx.signDirect.authInfo.serializeBinary(),
      [signBytes]
    );
  }
};
export const evmosBroadcast = async ({
  rawTx,
  chain,
}: {
  rawTx: ReturnType<typeof createTxRaw>;
  chain: AvailableChain;
}) => {
  const postOptions = {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: generatePostBodyBroadcast(rawTx),
  };

  let broadcastPost = await fetch(
    `https://api.evmos.interbloc.org${generateEndpointBroadcast()}`,
    postOptions
  );
  return await broadcastPost.json();

};

While Send Transaction on EVMOS through EVMOSJS got a signature verification issue

I am using the basic msgSend example in evmosjs but i am getting this error:

'signature verification failed; please verify account number (0) and chain-id (evmos_9000-4): feePayer pubkey EthPubKeySecp256k1{03C4EF884DB924BA9D37CD65EF081A6CB5357BB4FFB497AA8F3F2F21C9AD4378CB} is different from transaction pubkey EthPubKeySecp256k1{0362453C9F0E11C5ACCFBF0FA455C4681CCE8110633B68F57300D94D797DD4203D}: invalid pubkey [evmos/[email protected]/app/ante/eip712.go:287]: unauthorized'

I have tried changing the account number and the sequence and i have tried hard coding the EVMOS chain id but the error doesn't change so i am assuming it's not because of that.
Any help will be highly appreciated.

Screenshot from 2023-01-18 11-59-41

How to stake aevmos token to be a validator

I mean how to use evmosjs libary complete the following functions:

evmosd tx staking create-validator \
  --amount=1000000000000aevmos \
  --pubkey='{"@type":"/cosmos.crypto.ed25519.PubKey","key":"3YAmZANy26ad4otlglkx6sj1zIP8pCDQT8p5Rz4TGu4="}' \
  --moniker="ValidatorTest" \
  --chain-id="evmos_9000-1" \
  --commission-rate="0.05" \
  --commission-max-rate="0.20" \
  --commission-max-change-rate="0.01" \
  --min-self-delegation="1000000" \
  --gas-prices="0.025aevmos" \
  --gas="600000" \
  --from=validator \
  --keyring-backend=test \
  -b block

Address conversation

Is there any way to convert evmos address to evmosvaloper address?
In cli we use evmosd debug addr .... which gives us valoper address.
Can we do the same thing using evmosjs?
Any help will be appreciated.

Question regarding proposal submitions

What helper do we need to use to format the content for createTxMsgSubmitProposal
image

I'm trying to do this:

  const content = createMsgSubmitProposal(
      {
        type: "cosmos-sdk/TextProposal",
        value: {
          title: "title",
          description: "description",
        },
      },
      "azeta",
      "100",
      address
    );

And I'm then passing these params to createTxMsgSubmitProposal:

{
        content,
        initialDepositDenom: "azeta",
        initialDepositAmount: "100",
        proposer: address,
      }

With this I get this error:
image

And if I add the toArray and serialize functions manually to the content (because I'm not sure what helper to use to format the content) I get:
image

Error in react native typescript expo project

When I try to use @tharsis/address-converter in an react native expo typescript template project, I got this error:

ERROR ReferenceError: Can't find variable: Buffer
ERROR Invariant Violation: "main" has not been registered. This can happen if:

  • Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
  • A module failed to load due to an error and AppRegistry.registerComponent wasn't called.

How to derive the from address of a decoded transaction?

The bytesToLegacyTx function does not return the from address of the decoded transaction (see #16). How can the from address be derived from the result?

The encoded transaction below corresponds to this one on the block explorer.
"CpgDCuQCCh8vZXRoZXJtaW50LmV2bS52MS5Nc2dFdGhlcmV1bVR4EsACCvABChovZXRoZXJtaW50LmV2bS52MS5MZWdhY3lUeBLRAQgBEg01MDAwMDAwMDAwMDAwGP3wAiIqMHhEQzViQkRiNEE0YjA1MUJEQjg1Qjk1OWVCM2NCRDFjOEMwZDBjMTA1KgEwMkSiLLRlAAAAAAAAAAAAAAAAejzbI2T5I2mmAsroEWfQZ5CH5qMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAToBVUIgLB9VIqNf1ehvCU+duvAioON8KnG/dm6uyCXm5ftj6G5KIAfE/UfA1uCbKVxlsqYWPgu22ORLlm0aGC3CiKawhcmJEQAAAAAAgGVAGkIweGQ4MmQ0ZmU3YWMzYzczZDYzOTQ5MjBlZGU5NDUzNmQyZjM0ZmU2MTAxYTljZWM5NzQxZmM3OTIzNThjNjlmNWT6Py4KLC9ldGhlcm1pbnQuZXZtLnYxLkV4dGVuc2lvbk9wdGlvbnNFdGhlcmV1bVR4EiUSIwodCgdiYXNlY3JvEhIyMzYxNDUwMDAwMDAwMDAwMDAQ/fAC"

Thanks @hanchon

While Creating Validator We got an error

I am using the basic createvalidator example in evmosjs but i am getting this error:

'signature verification failed; please verify account number (0) and chain-id (evmos_9000-4): feePayer pubkey EthPubKeySecp256k1{03C4EF884DB924BA9D37CD65EF081A6CB5357BB4FFB497AA8F3F2F21C9AD4378CB} is different from transaction pubkey EthPubKeySecp256k1{0362453C9F0E11C5ACCFBF0FA455C4681CCE8110633B68F57300D94D797DD4203D}: invalid pubkey [evmos/[email protected]/app/ante/eip712.go:287]: unauthorized'

Anyone tell me that which pubKey is given in here:
Screenshot from 2023-01-19 16-11-42

Any help will be highly appreciated.

Keplr wallet signing.

let sign = await window?.keplr?.signDirect(
chain.cosmosChainId,
sender.accountAddress,
{
bodyBytes: msg.signDirect.body.serializeBinary(),
authInfoBytes: msg.signDirect.authInfo.serializeBinary(),
chainId: chain.cosmosChainId,
accountNumber: new Long(sender.accountNumber),
},
// @ts-expect-error the types are not updated on Keplr side
{ isEthereum: true },
)

if (sign !== undefined) {
let rawTx = createTxRaw(sign.signed.bodyBytes, sign.signed.authInfoBytes, [
new Uint8Array(Buffer.from(sign.signature.signature, 'base64')),
])

// Broadcast it
const postOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: generatePostBodyBroadcast(rawTx),
}

let broadcastPost = await fetch(
https://rest.cosmos.directory/evmos${generateEndpointBroadcast()},
postOptions,
)
let response = await broadcastPost.json()
console.log("response", response)
};

This is the code which I tried to run ibc transfer.
but how can we use this code on mobile version?
because there is no window.keplr on the mobile.
Please help me how can we run the transactions using keplr mobile,

Signing a transaction using a newly created wallet

For my use case I need to generate a random new wallet and sign an broadcast a txn using it, but I need to get some account info for it, accountNumber sequence , pubkey etc. To get this info this account must be used in some txn right? (I believe it's used to perfom state pruning).

So I sending some money to the account, now I get accountNumber and sequence but pubkey is empty. I tried to then sign a txn using accountNumber sequence and then pubkey from the wallet instead of from on-chain account info. But I get this error: 'pubKey does not match signer address evmos1kdhpt8q5vl3wg8myv680l0k0uwwvdcs25wx67a with signer index: 0: invalid pubkey'. I'm using @hanchon 's evmos-ts-wallet to sign and broadcast the transaction. Really appreciate any help. Thanks!

Add examples for custom evmos Msgs

I cannot create a transaction for custom evmos Msgs (namely MsgConvertCoin) using Metamask & EIP712.

is it possible that MsgConvertCoin is not a legacy amino cosmos msg, and therefore when it is broadcast to the evmos network, the network cannot read its type?

Below is the log I printed for txData for the MsgConvertCoin msg in Etheremint repo:

{
  "account_number": "0",
  "chain_id": "evmos_6666-1",
  "fee": {
    "amount": [
      {
        "amount": "0",
        "denom": "oraie"
      }
    ],
    "gas": "2000000"
  },
  "memo": "foobar",
  "msgs": [
    {
      "coin": {
        "amount": "1",
        "denom": "ibc/E8734BEF4ECF225B71825BC74DE30DCFF3644EAC9778FFD4EF9F94369B6C8377"
      },
      "receiver": "0x3C5C6b570C1DA469E8B24A2E8Ed33c278bDA3222",
      "sender": "evmos1sa2qx2k8je4fp83ww5es3h6khvyd40tf752s8j"
    }
  ],
  "sequence": "1"
}

As you can see, there's no type in the msgs array. Meanwhile, if I use a legacy msg like MsgSend, it appears:

{
  "account_number": "0",
  "chain_id": "evmos_6666-1",
  "fee": {
    "amount": [
      {
        "amount": "0",
        "denom": "oraie"
      }
    ],
    "gas": "2000000"
  },
  "memo": "foobar",
  "msgs": [
    {
      "type": "cosmos-sdk/MsgSend",
      "value": {
        "amount": [
          {
            "amount": "1",
            "denom": "ibc/E8734BEF4ECF225B71825BC74DE30DCFF3644EAC9778FFD4EF9F94369B6C8377"
          }
        ],
        "from_address": "evmos1sa2qx2k8je4fp83ww5es3h6khvyd40tf752s8j",
        "to_address": "evmos1sa2qx2k8je4fp83ww5es3h6khvyd40tf752s8j"
      }
    }
  ],
  "sequence": "1"
}

Also, is there an example for creating & broadcasting a custom msg using Keplr?

It would be great if we have more examples for custom evmos Msgs & a js function or library for signing evmos transactions. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.