import { wait } from "@anthem/utils" ; import TransportU2F from "@ledgerhq/hw-transport-u2f" ; import TransportHID from "@ledgerhq/hw-transport-webhid" ; import TransportUSB from "@ledgerhq/hw-transport-webusb" ; import OasisApp from "@oasisprotocol/ledger" ; import { LEDGER_ERRORS } from "constants/ledger-errors" ; import ENV from "tools/client-env" ; const GENESIS_HASH = "53852332637bacb61b91b6411ab4095168ba02a50be4c3f82448438826f23898" ; /** ===========================================================================
 * Oasis Ledger Utils
 * ---------------------------------------------------------------------------
 * Docs: https://github.com/Zondax/ledger-oasis-js
 * Example App: https://github.com/Zondax/ledger-oasis-js/blob/master/vue_example/components/LedgerExample.vue
 * Oasis RunKit Example: https://runkit.com/embed/jhwmrma4tdfb
 *
 * ---------------------------------------------------------------------------
 * [OASIS LEDGER TODO]:
 *
 * - How to determine transaction nonce?
 * - How to determine transaction gas fees/do users specify?
 *
 * TODO:
 * - Test the transfer transaction.
 * - Finish implementation for the delegation flow transactions.
 * - Test the delegation flow transactions.
 * - Enable for mainnet.
 * - Confirm with Oasis team.
 * ============================================================================
 */ const OASIS_API = { TRANSFER : `${ ENV . SERVER_URL }/api/oasis/transfer` , TRANSFER_SEND : `${ ENV . SERVER_URL }/api/oasis/transfer/send` , DELEGATE : `${ ENV . SERVER_URL }/api/oasis/delegate` , DELEGATE_SEND : `${ ENV . SERVER_URL }/api/oasis/transfer/send` , UNDELEGATE : `${ ENV . SERVER_URL }/api/oasis/undelegate` , UNDELEGATE_SEND : `${ ENV . SERVER_URL }/api/oasis/transfer/send` , REDELEGATE : `${ ENV . SERVER_URL }/api/oasis/redelegate` , REDELEGATE_SEND : `${ ENV . SERVER_URL }/api/oasis/redelegate/send` , } ; const headers = { Accept : "application/json" , "Content-Type" : "application/json" , } ; enum OasisTransactionMethod { TRANSFER = "staking.Transfer" , ADD_ESCROW = "staking.AddEscrow" , RECLAIM_ESCROW = "staking.ReclaimEscrow" , TAKE_ESCROW = "staking.TakeEscrow" } interface OasisTransactionPayload { nonce : number ; fee : { gas : number ; amount : Buffer ; } ; method : OasisTransactionMethod ; body : any ; } export interface IOasisTransactionReceipt { height : number ; hash : string ; } export interface OasisTransferArgs { from : string ; to : string ; amount : string ; } export interface OasisDelegateArgs { from : string ; to : string ; amount : string ; } export interface OasisUndelegateArgs { delegator : string ; validator : string ; amount : string ; } export interface OasisRedelegateArgs { delegator : string ; new_validator : string ; current_validator : string ; amount : string ; } interface IOasisLedger { connect ( ) : Promise < void > ; disconnect ( ) : void ; getAddress ( ) : Promise < string > ; getVersion ( ) : Promise < string > ; getPublicKey ( ) : Promise < string > ; transfer ( args ) : Promise < IOasisTransactionReceipt > ; delegate ( args ) : Promise < IOasisTransactionReceipt > ; undelegate ( args ) : Promise < IOasisTransactionReceipt > ; redelegate ( args ) : Promise < IOasisTransactionReceipt > ; } /** ===========================================================================
 * Oasis Ledger Class
 * ============================================================================
 */ class OasisLedgerClass implements IOasisLedger { private app : Nullable < any > = null ; private readonly path = [ 44 , 474 , 0 , 0 , 0 ] ; async connect ( ) { const transport = await getOasisLedgerTransport ( ) ; const app = new OasisApp ( transport ) ; this . app = app ; } disconnect ( ) { this . app = null ; } async showAddress ( ) { if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } await this . app . showAddressAndPubKey ( this . path ) ; } async getAddress ( ) { if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } const result = await this . app . getAddressAndPubKey ( this . path ) ; const address = result . bech32_address ; console . log ( `Oasis Address: ${ address }` ) ; return address ; } async getVersion ( ) { if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } const result = await this . app . getVersion ( ) ; const version = `${ result . major }.${ result . minor }.${ result . patch }` ; return version ; } async getPublicKey ( ) { if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } const result = await this . app . publicKey ( this . path ) ; console . log ( "PUBLIC KEY:" ) ; console . log ( result ) ; return result ; } async transfer ( args ) { console . log ( "Handling Oasis transfer transaction, args: " , args ) ; const tx = await this . getTransferPayload ( args ) ; const receipt = await this . signAndSendTransferTransaction ( tx ) ; return receipt ; } async delegate ( args ) { console . log ( "Handling Oasis delegate transaction, args: " , args ) ; const tx = await this . getDelegatePayload ( args ) ; const receipt = await this . signAndSendDelegationTransaction ( tx ) ; return receipt ; } async undelegate ( args ) { console . log ( "Handling Oasis undelegate transaction, args: " , args ) ; const tx = await this . getUndelegatePayload ( args ) ; const receipt = await this . signAndSendTransferTransaction ( tx ) ; return receipt ; } async redelegate ( args ) { console . log ( "Handling Oasis redelegate transaction, args: " , args ) ; return SampleTransactionReceipt ; } async getDelegatePayload ( args ) { console . log ( "getDelegatePayload: " , args ) ; const payload = { ... args , amount : parseInt ( args . amount ) , gas : { amount : 2000 , limit : 2269 } , } ; const response = await fetch ( OASIS_API . DELEGATE , { method : "POST" , headers , body : JSON . stringify ( payload ) , } ) ; const data = await response . json ( ) ; return data ; } async getUndelegatePayload ( args ) { console . log ( "getDelegatePayload: " , args ) ; const payload = { ... args , amount : parseInt ( args . amount ) , gas : { amount : 2000 , limit : 2269 } , } ; const response = await fetch ( OASIS_API . UNDELEGATE , { method : "POST" , headers , body : JSON . stringify ( payload ) , } ) ; const data = await response . json ( ) ; return data ; } async getTransferPayload ( args ) { console . log ( "getTransferPayload: " , args ) ; const payload = { ... args , amount : parseInt ( args . amount ) , gas : { amount : 10 , limit : 1500 } , } ; const response = await fetch ( OASIS_API . TRANSFER , { method : "POST" , headers , body : JSON . stringify ( payload ) , } ) ; const data = await response . json ( ) ; return data ; } async signAndSendTransferTransaction ( tx ) { console . log ( "signPayload: " , tx ) ; if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } const path = this . path ; const context = `oasis-core/consensus: tx for chain ${ GENESIS_HASH }` ; const publicKey = await this . app . publicKey ( path ) ; const result = await this . app . sign ( path , context , Buffer . from ( tx , "hex" ) ) ; const payload = { publicKey : Buffer . from ( publicKey . pk ) . toString ( "hex" ) , transaction : tx , signature : Buffer . from ( result . signature ) . toString ( "hex" ) , } ; const response = await fetch ( OASIS_API . TRANSFER_SEND , { method : "POST" , headers , body : JSON . stringify ( payload ) , } ) ; const data = await response . json ( ) ; return data ; } async signAndSendDelegationTransaction ( tx ) { if ( ! this . app ) { throw new Error ( "Oasis Ledger App not initialized yet!" ) ; } const path = this . path ; const context = `oasis-core/consensus: tx for chain ${ GENESIS_HASH }` ; const publicKey = await this . app . publicKey ( path ) ; const result = await this . app . sign ( path , context , Buffer . from ( tx , "hex" ) ) ; const payload = { publicKey : Buffer . from ( publicKey . pk ) . toString ( "hex" ) , transaction : tx , signature : Buffer . from ( result . signature ) . toString ( "hex" ) , } ; const response = await fetch ( OASIS_API . DELEGATE_SEND , { method : "POST" , headers , body : JSON . stringify ( payload ) , } ) ; const data = await response . json ( ) ; return data ; } } /** ===========================================================================
 * Transaction Utils
 * ============================================================================
 */ /**
 * Handle getting the Oasis Ledger transport.
 */ const getOasisLedgerTransport = async ( ) => { if ( window . HID ) { return TransportHID . create ( ) ; } else if ( window . USB ) { return TransportUSB . create ( ) ; } else if ( window . u2f ) { return TransportU2F . create ( ) ; } throw new Error ( LEDGER_ERRORS . BROWSER_NOT_SUPPORTED ) ; } ; /** ===========================================================================
 * Mock Oasis Ledger Class
 * ============================================================================
 */ const SampleTransactionReceipt = { height : 23807 , hash : "09525844d57e7ce4c270a79d769b2a7ab6e6d12d8d3ec2fa566eee2d0f89f02" , } ; class MockOasisLedgerModule implements IOasisLedger { async connect ( ) { await wait ( 1500 ) ; return ; } async disconnect ( ) { return ; } async getAddress ( ) { await wait ( 1500 ) ; return "oasis1qrllkgqgqheus3qvq69wzsmh7799agg8lgsyecfq" ; } async getVersion ( ) { await wait ( 1500 ) ; return "1.8.1" ; } async getPublicKey ( ) { await wait ( 1500 ) ; return "" ; } async encodeAndSignTransaction ( data ) { return "" ; } async broadcastTransaction ( data ) { return SampleTransactionReceipt ; } async transfer ( args ) { console . log ( "Handling Oasis transfer transaction, args: " , args ) ; await wait ( 1500 ) ; return SampleTransactionReceipt ; } async delegate ( args ) { console . log ( "Handling Oasis delegate transaction, args: " , args ) ; await wait ( 1500 ) ; return SampleTransactionReceipt ; } async undelegate ( args ) { console . log ( "Handling Oasis undelegate transaction, args: " , args ) ; await wait ( 1500 ) ; return SampleTransactionReceipt ; } async redelegate ( args ) { console . log ( "Handling Oasis redelegate transaction, args: " , args ) ; await wait ( 1500 ) ; return SampleTransactionReceipt ; } } /** ===========================================================================
 * Export
 * ============================================================================
 */ const oasisLedger = new OasisLedgerClass ( ) ; const mockOasisLedger = new MockOasisLedgerModule ( ) ; export default ENV . ENABLE_MOCK_APIS ? mockOasisLedger : oasisLedger ;