@portal-hq/web 3.4.1 → 3.5.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -777,3 +777,322 @@ export const mockClientMetadata = {
777
777
  },
778
778
  },
779
779
  }
780
+
781
+ // YieldXyz mock data
782
+ export const mockYieldXyzGetYieldsRequest = {
783
+ network: 'ethereum',
784
+ limit: 10,
785
+ offset: 0,
786
+ }
787
+
788
+ export const mockYieldXyzGetYieldsResponse = {
789
+ data: {
790
+ rawResponse: {
791
+ items: [
792
+ {
793
+ id: 'test-yield-id',
794
+ network: 'ethereum',
795
+ inputTokens: [
796
+ {
797
+ symbol: 'ETH',
798
+ name: 'Ethereum',
799
+ decimals: 18,
800
+ network: 'ethereum',
801
+ address: '0x0000000000000000000000000000000000000000',
802
+ logoURI: 'https://example.com/eth.png',
803
+ },
804
+ ],
805
+ outputToken: {
806
+ symbol: 'stETH',
807
+ name: 'Staked Ethereum',
808
+ decimals: 18,
809
+ network: 'ethereum',
810
+ address: '0x0000000000000000000000000000000000000001',
811
+ logoURI: 'https://example.com/steth.png',
812
+ },
813
+ token: {
814
+ symbol: 'ETH',
815
+ name: 'Ethereum',
816
+ decimals: 18,
817
+ network: 'ethereum',
818
+ address: '0x0000000000000000000000000000000000000000',
819
+ logoURI: 'https://example.com/eth.png',
820
+ },
821
+ rewardRate: {
822
+ total: 5.5,
823
+ rateType: 'APR' as const,
824
+ components: [],
825
+ },
826
+ status: {
827
+ enter: true,
828
+ exit: true,
829
+ },
830
+ metadata: {
831
+ name: 'Test Yield',
832
+ logoURI: 'https://example.com/logo.png',
833
+ description: 'Test yield opportunity',
834
+ documentation: 'https://example.com/docs',
835
+ underMaintenance: false,
836
+ deprecated: false,
837
+ supportedStandards: ['ERC20'],
838
+ },
839
+ mechanics: {
840
+ type: 'staking' as const,
841
+ requiresValidatorSelection: false,
842
+ rewardSchedule: 'block' as const,
843
+ rewardClaiming: 'auto' as const,
844
+ gasFeeToken: {
845
+ symbol: 'ETH',
846
+ name: 'Ethereum',
847
+ decimals: 18,
848
+ },
849
+ entryLimits: {
850
+ minimum: '0.01',
851
+ },
852
+ supportsLedgerWalletApi: true,
853
+ arguments: {
854
+ enter: { fields: [] },
855
+ exit: { fields: [] },
856
+ },
857
+ possibleFeeTakingMechanisms: {
858
+ depositFee: false,
859
+ managementFee: false,
860
+ performanceFee: false,
861
+ validatorRebates: false,
862
+ },
863
+ },
864
+ providerId: 'test-provider',
865
+ tags: ['defi', 'staking'],
866
+ },
867
+ ],
868
+ limit: 10,
869
+ offset: 0,
870
+ total: 1,
871
+ },
872
+ },
873
+ }
874
+
875
+ export const mockYieldXyzEnterRequest = {
876
+ yieldId: 'test-yield-id',
877
+ address: mockAddress,
878
+ arguments: {
879
+ amount: '1.0',
880
+ },
881
+ }
882
+
883
+ export const mockYieldXyzEnterResponse = {
884
+ data: {
885
+ rawResponse: {
886
+ id: 'test-action-id',
887
+ intent: 'enter' as const,
888
+ type: 'STAKE' as const,
889
+ yieldId: 'test-yield-id',
890
+ address: mockAddress,
891
+ amount: '1.0',
892
+ amountRaw: '1000000000000000000',
893
+ amountUsd: '3000',
894
+ transactions: [
895
+ {
896
+ id: 'test-tx-id',
897
+ title: 'Stake ETH',
898
+ network: 'ethereum',
899
+ status: 'WAITING_FOR_SIGNATURE' as const,
900
+ type: 'STAKE' as const,
901
+ createdAt: '2024-01-01T00:00:00Z',
902
+ stepIndex: 0,
903
+ },
904
+ ],
905
+ executionPattern: 'synchronous' as const,
906
+ createdAt: '2024-01-01T00:00:00Z',
907
+ status: 'CREATED' as const,
908
+ },
909
+ },
910
+ }
911
+
912
+ export const mockYieldXyzExitRequest = {
913
+ yieldId: 'test-yield-id',
914
+ address: mockAddress,
915
+ arguments: {
916
+ amount: '1.0',
917
+ },
918
+ }
919
+
920
+ export const mockYieldXyzExitResponse = {
921
+ data: {
922
+ rawResponse: {
923
+ id: 'test-action-id',
924
+ intent: 'exit' as const,
925
+ type: 'UNSTAKE' as const,
926
+ yieldId: 'test-yield-id',
927
+ address: mockAddress,
928
+ amount: '1.0',
929
+ amountRaw: '1000000000000000000',
930
+ amountUsd: '3000',
931
+ transactions: [
932
+ {
933
+ id: 'test-tx-id',
934
+ title: 'Unstake ETH',
935
+ network: 'ethereum',
936
+ status: 'WAITING_FOR_SIGNATURE' as const,
937
+ type: 'UNSTAKE' as const,
938
+ createdAt: '2024-01-01T00:00:00Z',
939
+ stepIndex: 0,
940
+ },
941
+ ],
942
+ executionPattern: 'synchronous' as const,
943
+ createdAt: '2024-01-01T00:00:00Z',
944
+ status: 'CREATED' as const,
945
+ },
946
+ },
947
+ }
948
+
949
+ export const mockYieldXyzGetBalancesRequest = {
950
+ queries: [
951
+ {
952
+ address: mockAddress,
953
+ network: 'ethereum',
954
+ yieldId: 'test-yield-id',
955
+ },
956
+ ],
957
+ }
958
+
959
+ export const mockYieldXyzGetBalancesResponse = {
960
+ data: {
961
+ rawResponse: {
962
+ items: [
963
+ {
964
+ yieldId: 'test-yield-id',
965
+ balances: [
966
+ {
967
+ address: mockAddress,
968
+ amount: '1.5',
969
+ amountRaw: '1500000000000000000',
970
+ type: 'staked',
971
+ token: {
972
+ address: '0x0000000000000000000000000000000000000001',
973
+ symbol: 'stETH',
974
+ name: 'Staked Ethereum',
975
+ decimals: 18,
976
+ network: 'ethereum',
977
+ },
978
+ amountUsd: '4500',
979
+ isEarning: true,
980
+ },
981
+ ],
982
+ },
983
+ ],
984
+ },
985
+ },
986
+ }
987
+
988
+ export const mockYieldXyzGetHistoricalActionsRequest = {
989
+ address: mockAddress,
990
+ limit: 10,
991
+ offset: 0,
992
+ }
993
+
994
+ export const mockYieldXyzGetHistoricalActionsResponse = {
995
+ data: {
996
+ rawResponse: {
997
+ items: [
998
+ {
999
+ id: 'test-action-id-1',
1000
+ intent: 'enter' as const,
1001
+ type: 'STAKE' as const,
1002
+ yieldId: 'test-yield-id',
1003
+ address: mockAddress,
1004
+ amount: '1.0',
1005
+ amountRaw: '1000000000000000000',
1006
+ amountUsd: '3000',
1007
+ transactions: [],
1008
+ executionPattern: 'synchronous' as const,
1009
+ createdAt: '2024-01-01T00:00:00Z',
1010
+ completedAt: '2024-01-01T00:05:00Z',
1011
+ status: 'SUCCESS' as const,
1012
+ },
1013
+ ],
1014
+ total: 1,
1015
+ offset: 0,
1016
+ limit: 10,
1017
+ },
1018
+ },
1019
+ }
1020
+
1021
+ export const mockYieldXyzManageYieldRequest = {
1022
+ yieldId: 'test-yield-id',
1023
+ address: mockAddress,
1024
+ action: 'CLAIM_REWARDS' as const,
1025
+ passthrough: 'test-passthrough',
1026
+ }
1027
+
1028
+ export const mockYieldXyzManageYieldResponse = {
1029
+ data: {
1030
+ rawResponse: {
1031
+ id: 'test-action-id',
1032
+ intent: 'manage' as const,
1033
+ type: 'CLAIM_REWARDS' as const,
1034
+ yieldId: 'test-yield-id',
1035
+ address: mockAddress,
1036
+ amount: '0.1',
1037
+ amountRaw: '100000000000000000',
1038
+ amountUsd: '300',
1039
+ transactions: [
1040
+ {
1041
+ id: 'test-tx-id',
1042
+ title: 'Claim Rewards',
1043
+ network: 'ethereum',
1044
+ status: 'WAITING_FOR_SIGNATURE' as const,
1045
+ type: 'CLAIM_REWARDS' as const,
1046
+ createdAt: '2024-01-01T00:00:00Z',
1047
+ stepIndex: 0,
1048
+ },
1049
+ ],
1050
+ executionPattern: 'synchronous' as const,
1051
+ createdAt: '2024-01-01T00:00:00Z',
1052
+ status: 'CREATED' as const,
1053
+ },
1054
+ },
1055
+ }
1056
+
1057
+ export const mockYieldXyzTrackTransactionRequest = {
1058
+ transactionId: 'test-tx-id',
1059
+ hash: '0x1234567890abcdef',
1060
+ }
1061
+
1062
+ export const mockYieldXyzTrackTransactionResponse = {
1063
+ data: {
1064
+ rawResponse: {
1065
+ id: 'test-tx-id',
1066
+ title: 'Stake ETH',
1067
+ network: 'ethereum',
1068
+ status: 'BROADCASTED' as const,
1069
+ type: 'STAKE' as const,
1070
+ hash: '0x1234567890abcdef',
1071
+ createdAt: '2024-01-01T00:00:00Z',
1072
+ broadcastedAt: '2024-01-01T00:01:00Z',
1073
+ stepIndex: 0,
1074
+ gasEstimate: '21000',
1075
+ },
1076
+ },
1077
+ }
1078
+
1079
+ export const mockYieldXyzGetTransactionResponse = {
1080
+ data: {
1081
+ rawResponse: {
1082
+ id: 'test-tx-id',
1083
+ title: 'Stake ETH',
1084
+ network: 'ethereum',
1085
+ status: 'CONFIRMED' as const,
1086
+ type: 'STAKE' as const,
1087
+ hash: '0x1234567890abcdef',
1088
+ createdAt: '2024-01-01T00:00:00Z',
1089
+ broadcastedAt: '2024-01-01T00:01:00Z',
1090
+ stepIndex: 0,
1091
+ gasEstimate: '21000',
1092
+ },
1093
+ },
1094
+ metadata: {
1095
+ clientId: 'test-client-id',
1096
+ transactionId: 'test-tx-id',
1097
+ },
1098
+ }
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from '@solana/web3.js'
7
7
 
8
8
  import {
9
- Asset,
9
+ GetAssetsResponse,
10
10
  BuiltTransaction,
11
11
  EjectPrivateKeysResult,
12
12
  EvaluateTransactionOperationType,
@@ -43,6 +43,7 @@ import {
43
43
  } from '../types'
44
44
  import Mpc from './mpc'
45
45
  import Provider, { RequestMethod } from './provider'
46
+ import Yield from './integrations/yield'
46
47
 
47
48
  class Portal {
48
49
  public address?: string
@@ -54,6 +55,7 @@ class Portal {
54
55
  public passkeyConfig?: PasskeyConfig
55
56
  public host: string
56
57
  public mpc: Mpc
58
+ public yield: Yield
57
59
  public mpcHost: string
58
60
  public mpcVersion: string
59
61
  public provider: Provider
@@ -105,6 +107,8 @@ class Portal {
105
107
  portal: this,
106
108
  })
107
109
 
110
+ this.yield = new Yield({ mpc: this.mpc })
111
+
108
112
  this.provider = new Provider({
109
113
  portal: this,
110
114
  chainId: chainId ? Number(chainId) : undefined,
@@ -862,7 +866,7 @@ class Portal {
862
866
  public async getAssets(
863
867
  chainId: string,
864
868
  includeNfts = false,
865
- ): Promise<Asset[]> {
869
+ ): Promise<GetAssetsResponse> {
866
870
  return this.mpc?.getAssets(chainId, includeNfts)
867
871
  }
868
872
 
@@ -1018,6 +1022,25 @@ class Portal {
1018
1022
  }
1019
1023
  }
1020
1024
 
1025
+ export {
1026
+ YieldXyzEnterRequest,
1027
+ YieldXyzEnterYieldResponse,
1028
+ YieldXyzExitRequest,
1029
+ YieldXyzExitResponse,
1030
+ YieldXyzGetBalancesRequest,
1031
+ YieldXyzGetBalancesResponse,
1032
+ YieldXyzGetHistoricalActionsRequest,
1033
+ YieldXyzGetHistoricalActionsResponse,
1034
+ YieldXyzGetTransactionRequest,
1035
+ YieldXyzGetTransactionResponse,
1036
+ YieldXyzGetYieldsRequest,
1037
+ YieldXyzGetYieldsResponse,
1038
+ YieldXyzManageYieldRequest,
1039
+ YieldXyzManageYieldResponse,
1040
+ YieldXyzTrackTransactionRequest,
1041
+ YieldXyzTrackTransactionResponse,
1042
+ } from 'yieldxyz-types'
1043
+
1021
1044
  export { MpcError, MpcErrorCodes } from './mpc'
1022
1045
  export {
1023
1046
  type Address,
@@ -0,0 +1,14 @@
1
+ import Mpc from '../../mpc'
2
+ import YieldXyz from './yieldxyz'
3
+
4
+ /**
5
+ * This class is a container for the YieldXyz class.
6
+ * In the future, Yield domain logic should be here.
7
+ */
8
+ export default class Yield {
9
+ public yieldXyz: YieldXyz
10
+
11
+ constructor({ mpc }: { mpc: Mpc }) {
12
+ this.yieldXyz = new YieldXyz({ mpc })
13
+ }
14
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import YieldXyz from './yieldxyz'
6
+ import Mpc from '../../mpc'
7
+ import portalMock from '../../__mocks/portal/portal'
8
+ import {
9
+ mockHost,
10
+ mockYieldXyzGetYieldsRequest,
11
+ mockYieldXyzGetYieldsResponse,
12
+ mockYieldXyzEnterRequest,
13
+ mockYieldXyzEnterResponse,
14
+ mockYieldXyzExitRequest,
15
+ mockYieldXyzExitResponse,
16
+ mockYieldXyzGetBalancesRequest,
17
+ mockYieldXyzGetBalancesResponse,
18
+ mockYieldXyzGetHistoricalActionsRequest,
19
+ mockYieldXyzGetHistoricalActionsResponse,
20
+ mockYieldXyzManageYieldRequest,
21
+ mockYieldXyzManageYieldResponse,
22
+ mockYieldXyzTrackTransactionRequest,
23
+ mockYieldXyzTrackTransactionResponse,
24
+ mockYieldXyzGetTransactionResponse,
25
+ } from '../../__mocks/constants'
26
+
27
+ describe('YieldXyz', () => {
28
+ let yieldXyz: YieldXyz
29
+ let mpc: Mpc
30
+
31
+ beforeEach(() => {
32
+ jest.clearAllMocks()
33
+
34
+ portalMock.host = mockHost
35
+ mpc = new Mpc({
36
+ portal: portalMock,
37
+ })
38
+
39
+ yieldXyz = new YieldXyz({ mpc })
40
+ })
41
+
42
+ describe('discover', () => {
43
+ it('should call mpc.getYieldXyzYields with the correct arguments', async () => {
44
+ const spy = jest
45
+ .spyOn(mpc, 'getYieldXyzYields')
46
+ .mockResolvedValue(mockYieldXyzGetYieldsResponse)
47
+
48
+ const result = await yieldXyz.discover(mockYieldXyzGetYieldsRequest)
49
+
50
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzGetYieldsRequest)
51
+ expect(result).toEqual(mockYieldXyzGetYieldsResponse)
52
+ })
53
+
54
+ it('should propagate errors from mpc.getYieldXyzYields', async () => {
55
+ const error = new Error('Test error')
56
+ jest.spyOn(mpc, 'getYieldXyzYields').mockRejectedValue(error)
57
+
58
+ await expect(
59
+ yieldXyz.discover(mockYieldXyzGetYieldsRequest),
60
+ ).rejects.toThrow('Test error')
61
+ })
62
+ })
63
+
64
+ describe('enter', () => {
65
+ it('should call mpc.enterYieldXyzYield with the correct arguments', async () => {
66
+ const spy = jest
67
+ .spyOn(mpc, 'enterYieldXyzYield')
68
+ .mockResolvedValue(mockYieldXyzEnterResponse)
69
+
70
+ const result = await yieldXyz.enter(mockYieldXyzEnterRequest)
71
+
72
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzEnterRequest)
73
+ expect(result).toEqual(mockYieldXyzEnterResponse)
74
+ })
75
+
76
+ it('should propagate errors from mpc.enterYieldXyzYield', async () => {
77
+ const error = new Error('Test error')
78
+ jest.spyOn(mpc, 'enterYieldXyzYield').mockRejectedValue(error)
79
+
80
+ await expect(yieldXyz.enter(mockYieldXyzEnterRequest)).rejects.toThrow(
81
+ 'Test error',
82
+ )
83
+ })
84
+ })
85
+
86
+ describe('exit', () => {
87
+ it('should call mpc.exitYieldXyzYield with the correct arguments', async () => {
88
+ const spy = jest
89
+ .spyOn(mpc, 'exitYieldXyzYield')
90
+ .mockResolvedValue(mockYieldXyzExitResponse)
91
+
92
+ const result = await yieldXyz.exit(mockYieldXyzExitRequest)
93
+
94
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzExitRequest)
95
+ expect(result).toEqual(mockYieldXyzExitResponse)
96
+ })
97
+
98
+ it('should propagate errors from mpc.exitYieldXyzYield', async () => {
99
+ const error = new Error('Test error')
100
+ jest.spyOn(mpc, 'exitYieldXyzYield').mockRejectedValue(error)
101
+
102
+ await expect(yieldXyz.exit(mockYieldXyzExitRequest)).rejects.toThrow(
103
+ 'Test error',
104
+ )
105
+ })
106
+ })
107
+
108
+ describe('getBalances', () => {
109
+ it('should call mpc.getYieldXyzBalances with the correct arguments', async () => {
110
+ const spy = jest
111
+ .spyOn(mpc, 'getYieldXyzBalances')
112
+ .mockResolvedValue(mockYieldXyzGetBalancesResponse)
113
+
114
+ const result = await yieldXyz.getBalances(mockYieldXyzGetBalancesRequest)
115
+
116
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzGetBalancesRequest)
117
+ expect(result).toEqual(mockYieldXyzGetBalancesResponse)
118
+ })
119
+
120
+ it('should propagate errors from mpc.getYieldXyzBalances', async () => {
121
+ const error = new Error('Test error')
122
+ jest.spyOn(mpc, 'getYieldXyzBalances').mockRejectedValue(error)
123
+
124
+ await expect(
125
+ yieldXyz.getBalances(mockYieldXyzGetBalancesRequest),
126
+ ).rejects.toThrow('Test error')
127
+ })
128
+ })
129
+
130
+ describe('getHistoricalActions', () => {
131
+ it('should call mpc.getYieldXyzHistoricalActions with the correct arguments', async () => {
132
+ const spy = jest
133
+ .spyOn(mpc, 'getYieldXyzHistoricalActions')
134
+ .mockResolvedValue(mockYieldXyzGetHistoricalActionsResponse)
135
+
136
+ const result = await yieldXyz.getHistoricalActions(
137
+ mockYieldXyzGetHistoricalActionsRequest,
138
+ )
139
+
140
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzGetHistoricalActionsRequest)
141
+ expect(result).toEqual(mockYieldXyzGetHistoricalActionsResponse)
142
+ })
143
+
144
+ it('should propagate errors from mpc.getYieldXyzHistoricalActions', async () => {
145
+ const error = new Error('Test error')
146
+ jest.spyOn(mpc, 'getYieldXyzHistoricalActions').mockRejectedValue(error)
147
+
148
+ await expect(
149
+ yieldXyz.getHistoricalActions(mockYieldXyzGetHistoricalActionsRequest),
150
+ ).rejects.toThrow('Test error')
151
+ })
152
+ })
153
+
154
+ describe('manage', () => {
155
+ it('should call mpc.manageYieldXyzYield with the correct arguments', async () => {
156
+ const spy = jest
157
+ .spyOn(mpc, 'manageYieldXyzYield')
158
+ .mockResolvedValue(mockYieldXyzManageYieldResponse)
159
+
160
+ const result = await yieldXyz.manage(mockYieldXyzManageYieldRequest)
161
+
162
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzManageYieldRequest)
163
+ expect(result).toEqual(mockYieldXyzManageYieldResponse)
164
+ })
165
+
166
+ it('should propagate errors from mpc.manageYieldXyzYield', async () => {
167
+ const error = new Error('Test error')
168
+ jest.spyOn(mpc, 'manageYieldXyzYield').mockRejectedValue(error)
169
+
170
+ await expect(
171
+ yieldXyz.manage(mockYieldXyzManageYieldRequest),
172
+ ).rejects.toThrow('Test error')
173
+ })
174
+ })
175
+
176
+ describe('track', () => {
177
+ it('should call mpc.trackYieldXyzTransaction with the correct arguments', async () => {
178
+ const spy = jest
179
+ .spyOn(mpc, 'trackYieldXyzTransaction')
180
+ .mockResolvedValue(mockYieldXyzTrackTransactionResponse)
181
+
182
+ const result = await yieldXyz.track(mockYieldXyzTrackTransactionRequest)
183
+
184
+ expect(spy).toHaveBeenCalledWith(mockYieldXyzTrackTransactionRequest)
185
+ expect(result).toEqual(mockYieldXyzTrackTransactionResponse)
186
+ })
187
+
188
+ it('should propagate errors from mpc.trackYieldXyzTransaction', async () => {
189
+ const error = new Error('Test error')
190
+ jest.spyOn(mpc, 'trackYieldXyzTransaction').mockRejectedValue(error)
191
+
192
+ await expect(
193
+ yieldXyz.track(mockYieldXyzTrackTransactionRequest),
194
+ ).rejects.toThrow('Test error')
195
+ })
196
+ })
197
+
198
+ describe('getTransaction', () => {
199
+ it('should call mpc.getYieldXyzTransaction with the correct arguments', async () => {
200
+ const transactionId = 'test-tx-id'
201
+ const spy = jest
202
+ .spyOn(mpc, 'getYieldXyzTransaction')
203
+ .mockResolvedValue(mockYieldXyzGetTransactionResponse)
204
+
205
+ const result = await yieldXyz.getTransaction(transactionId)
206
+
207
+ expect(spy).toHaveBeenCalledWith(transactionId)
208
+ expect(result).toEqual(mockYieldXyzGetTransactionResponse)
209
+ })
210
+
211
+ it('should propagate errors from mpc.getYieldXyzTransaction', async () => {
212
+ const error = new Error('Test error')
213
+ jest.spyOn(mpc, 'getYieldXyzTransaction').mockRejectedValue(error)
214
+
215
+ await expect(yieldXyz.getTransaction('test-tx-id')).rejects.toThrow(
216
+ 'Test error',
217
+ )
218
+ })
219
+ })
220
+ })