@@ -6,14 +6,19 @@ import {
66 calculateBytesize ,
77 countOpcodes ,
88 generateRedeemScript ,
9+ hash256 ,
910 Script ,
1011 scriptToBytecode ,
1112} from '@cashscript/utils' ;
1213import { Transaction } from './Transaction.js' ;
1314import { Argument , encodeArgument } from './Argument.js' ;
14- import { ContractOptions , Utxo } from './interfaces.js' ;
15+ import {
16+ Unlocker , ContractOptions , GenerateUnlockingBytecodeOptions , Utxo ,
17+ } from './interfaces.js' ;
1518import NetworkProvider from './network/NetworkProvider.js' ;
16- import { scriptToAddress } from './utils.js' ;
19+ import {
20+ addressToLockScript , createInputScript , createSighashPreimage , scriptToAddress ,
21+ } from './utils.js' ;
1722import SignatureTemplate from './SignatureTemplate.js' ;
1823import { ElectrumNetworkProvider } from './network/index.js' ;
1924
@@ -25,9 +30,8 @@ export class Contract {
2530 bytesize : number ;
2631 opcount : number ;
2732
28- functions : {
29- [ name : string ] : ContractFunction ,
30- } ;
33+ functions : Record < string , ContractFunction > ;
34+ unlock : Record < string , ContractUnlocker > ;
3135
3236 private redeemScript : Script ;
3337 private provider : NetworkProvider ;
@@ -38,10 +42,8 @@ export class Contract {
3842 constructorArgs : Argument [ ] ,
3943 private options ?: ContractOptions ,
4044 ) {
41- const defaultProvider = new ElectrumNetworkProvider ( ) ;
42- const defaultAddressType = 'p2sh32' ;
43- this . provider = this . options ?. provider ?? defaultProvider ;
44- this . addressType = this . options ?. addressType ?? defaultAddressType ;
45+ this . provider = this . options ?. provider ?? new ElectrumNetworkProvider ( ) ;
46+ this . addressType = this . options ?. addressType ?? 'p2sh32' ;
4547
4648 const expectedProperties = [ 'abi' , 'bytecode' , 'constructorInputs' , 'contractName' ] ;
4749 if ( ! expectedProperties . every ( ( property ) => property in artifact ) ) {
@@ -79,6 +81,18 @@ export class Contract {
7981 } ) ;
8082 }
8183
84+ // Populate the functions object with the contract's functions
85+ // (with a special case for single function, which has no "function selector")
86+ this . unlock = { } ;
87+ if ( artifact . abi . length === 1 ) {
88+ const f = artifact . abi [ 0 ] ;
89+ this . unlock [ f . name ] = this . createUnlocker ( f ) ;
90+ } else {
91+ artifact . abi . forEach ( ( f , i ) => {
92+ this . unlock [ f . name ] = this . createUnlocker ( f , i ) ;
93+ } ) ;
94+ }
95+
8296 this . name = artifact . contractName ;
8397 this . address = scriptToAddress ( this . redeemScript , this . provider . network , this . addressType , false ) ;
8498 this . tokenAddress = scriptToAddress ( this . redeemScript , this . provider . network , this . addressType , true ) ;
@@ -116,6 +130,39 @@ export class Contract {
116130 ) ;
117131 } ;
118132 }
133+
134+ private createUnlocker ( abiFunction : AbiFunction , selector ?: number ) : ContractUnlocker {
135+ return ( ...args : Argument [ ] ) => {
136+ const bytecode = scriptToBytecode ( this . redeemScript ) ;
137+
138+ const encodedArgs = args
139+ . map ( ( arg , i ) => encodeArgument ( arg , abiFunction . inputs [ i ] . type ) ) ;
140+
141+ const generateUnlockingBytecode = (
142+ { transaction, sourceOutputs, inputIndex } : GenerateUnlockingBytecodeOptions ,
143+ ) : Uint8Array => {
144+ const completeArgs = encodedArgs . map ( ( arg ) => {
145+ if ( ! ( arg instanceof SignatureTemplate ) ) return arg ;
146+
147+ const preimage = createSighashPreimage ( transaction , sourceOutputs , inputIndex , bytecode , arg . getHashType ( ) ) ;
148+ const sighash = hash256 ( preimage ) ;
149+
150+ return arg . generateSignature ( sighash ) ;
151+ } ) ;
152+
153+ const unlockingBytecode = createInputScript (
154+ this . redeemScript , completeArgs , selector ,
155+ ) ;
156+
157+ return unlockingBytecode ;
158+ } ;
159+
160+ const generateLockingBytecode = ( ) : Uint8Array => addressToLockScript ( this . address ) ;
161+
162+ return { generateUnlockingBytecode, generateLockingBytecode } ;
163+ } ;
164+ }
119165}
120166
121167export type ContractFunction = ( ...args : Argument [ ] ) => Transaction ;
168+ export type ContractUnlocker = ( ...args : Argument [ ] ) => Unlocker ;
0 commit comments