@towns-protocol/web3 0.0.191

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.
Files changed (327) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +3 -0
  3. package/dist/ContractHelpers.d.ts +18 -0
  4. package/dist/ContractHelpers.d.ts.map +1 -0
  5. package/dist/ContractHelpers.js +75 -0
  6. package/dist/ContractHelpers.js.map +1 -0
  7. package/dist/ContractTypes.d.ts +143 -0
  8. package/dist/ContractTypes.d.ts.map +1 -0
  9. package/dist/ContractTypes.js +65 -0
  10. package/dist/ContractTypes.js.map +1 -0
  11. package/dist/ConvertersEntitlements.d.ts +10 -0
  12. package/dist/ConvertersEntitlements.d.ts.map +1 -0
  13. package/dist/ConvertersEntitlements.js +120 -0
  14. package/dist/ConvertersEntitlements.js.map +1 -0
  15. package/dist/ConvertersRoles.d.ts +8 -0
  16. package/dist/ConvertersRoles.d.ts.map +1 -0
  17. package/dist/ConvertersRoles.js +94 -0
  18. package/dist/ConvertersRoles.js.map +1 -0
  19. package/dist/DelegateRegistry.d.ts +3 -0
  20. package/dist/DelegateRegistry.d.ts.map +1 -0
  21. package/dist/DelegateRegistry.js +72 -0
  22. package/dist/DelegateRegistry.js.map +1 -0
  23. package/dist/EntitlementCache.d.ts +20 -0
  24. package/dist/EntitlementCache.d.ts.map +1 -0
  25. package/dist/EntitlementCache.js +41 -0
  26. package/dist/EntitlementCache.js.map +1 -0
  27. package/dist/EntitlementCache.test.d.ts +5 -0
  28. package/dist/EntitlementCache.test.d.ts.map +1 -0
  29. package/dist/EntitlementCache.test.js +130 -0
  30. package/dist/EntitlementCache.test.js.map +1 -0
  31. package/dist/ISpaceDapp.d.ts +193 -0
  32. package/dist/ISpaceDapp.d.ts.map +1 -0
  33. package/dist/ISpaceDapp.js +2 -0
  34. package/dist/ISpaceDapp.js.map +1 -0
  35. package/dist/IStaticContractsInfo.d.ts +28 -0
  36. package/dist/IStaticContractsInfo.d.ts.map +1 -0
  37. package/dist/IStaticContractsInfo.js +13 -0
  38. package/dist/IStaticContractsInfo.js.map +1 -0
  39. package/dist/LocalhostWeb3Provider.d.ts +15 -0
  40. package/dist/LocalhostWeb3Provider.d.ts.map +1 -0
  41. package/dist/LocalhostWeb3Provider.js +60 -0
  42. package/dist/LocalhostWeb3Provider.js.map +1 -0
  43. package/dist/MockCrossChainEntitlement.d.ts +6 -0
  44. package/dist/MockCrossChainEntitlement.d.ts.map +1 -0
  45. package/dist/MockCrossChainEntitlement.js +104 -0
  46. package/dist/MockCrossChainEntitlement.js.map +1 -0
  47. package/dist/MockERC1155.d.ts +6 -0
  48. package/dist/MockERC1155.d.ts.map +1 -0
  49. package/dist/MockERC1155.js +518 -0
  50. package/dist/MockERC1155.js.map +1 -0
  51. package/dist/MockERC20.d.ts +6 -0
  52. package/dist/MockERC20.d.ts.map +1 -0
  53. package/dist/MockERC20.js +574 -0
  54. package/dist/MockERC20.js.map +1 -0
  55. package/dist/MockERC721A.d.ts +1563 -0
  56. package/dist/MockERC721A.d.ts.map +1 -0
  57. package/dist/MockERC721A.js +1913 -0
  58. package/dist/MockERC721A.js.map +1 -0
  59. package/dist/RiverRegistryFactory.d.ts +5 -0
  60. package/dist/RiverRegistryFactory.d.ts.map +1 -0
  61. package/dist/RiverRegistryFactory.js +5 -0
  62. package/dist/RiverRegistryFactory.js.map +1 -0
  63. package/dist/SpaceDappFactory.d.ts +5 -0
  64. package/dist/SpaceDappFactory.d.ts.map +1 -0
  65. package/dist/SpaceDappFactory.js +8 -0
  66. package/dist/SpaceDappFactory.js.map +1 -0
  67. package/dist/TestCrossChainEntitlement.d.ts +14 -0
  68. package/dist/TestCrossChainEntitlement.d.ts.map +1 -0
  69. package/dist/TestCrossChainEntitlement.js +100 -0
  70. package/dist/TestCrossChainEntitlement.js.map +1 -0
  71. package/dist/TestEthBalance.d.ts +60 -0
  72. package/dist/TestEthBalance.d.ts.map +1 -0
  73. package/dist/TestEthBalance.js +94 -0
  74. package/dist/TestEthBalance.js.map +1 -0
  75. package/dist/TestGatingERC1155.d.ts +17 -0
  76. package/dist/TestGatingERC1155.d.ts.map +1 -0
  77. package/dist/TestGatingERC1155.js +101 -0
  78. package/dist/TestGatingERC1155.js.map +1 -0
  79. package/dist/TestGatingERC20.d.ts +17 -0
  80. package/dist/TestGatingERC20.d.ts.map +1 -0
  81. package/dist/TestGatingERC20.js +149 -0
  82. package/dist/TestGatingERC20.js.map +1 -0
  83. package/dist/TestGatingNFT.d.ts +17 -0
  84. package/dist/TestGatingNFT.d.ts.map +1 -0
  85. package/dist/TestGatingNFT.js +140 -0
  86. package/dist/TestGatingNFT.js.map +1 -0
  87. package/dist/TestGatingUtils.d.ts +15 -0
  88. package/dist/TestGatingUtils.d.ts.map +1 -0
  89. package/dist/TestGatingUtils.js +112 -0
  90. package/dist/TestGatingUtils.js.map +1 -0
  91. package/dist/Utils.d.ts +59 -0
  92. package/dist/Utils.d.ts.map +1 -0
  93. package/dist/Utils.js +122 -0
  94. package/dist/Utils.js.map +1 -0
  95. package/dist/Web3Constants.d.ts +6 -0
  96. package/dist/Web3Constants.d.ts.map +1 -0
  97. package/dist/Web3Constants.js +7 -0
  98. package/dist/Web3Constants.js.map +1 -0
  99. package/dist/Web3Constants.test.d.ts +2 -0
  100. package/dist/Web3Constants.test.d.ts.map +1 -0
  101. package/dist/Web3Constants.test.js +15 -0
  102. package/dist/Web3Constants.test.js.map +1 -0
  103. package/dist/chain.d.ts +65 -0
  104. package/dist/chain.d.ts.map +1 -0
  105. package/dist/chain.js +48 -0
  106. package/dist/chain.js.map +1 -0
  107. package/dist/entitlement.d.ts +146 -0
  108. package/dist/entitlement.d.ts.map +1 -0
  109. package/dist/entitlement.js +931 -0
  110. package/dist/entitlement.js.map +1 -0
  111. package/dist/entitlement.test.d.ts +2 -0
  112. package/dist/entitlement.test.d.ts.map +1 -0
  113. package/dist/entitlement.test.js +1743 -0
  114. package/dist/entitlement.test.js.map +1 -0
  115. package/dist/error-types.d.ts +7 -0
  116. package/dist/error-types.d.ts.map +1 -0
  117. package/dist/error-types.js +13 -0
  118. package/dist/error-types.js.map +1 -0
  119. package/dist/index.d.ts +76 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +76 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/types.d.ts +29 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +2 -0
  126. package/dist/types.js.map +1 -0
  127. package/dist/utils.test.d.ts +2 -0
  128. package/dist/utils.test.d.ts.map +1 -0
  129. package/dist/utils.test.js +44 -0
  130. package/dist/utils.test.js.map +1 -0
  131. package/dist/v3/BaseContractShim.d.ts +28 -0
  132. package/dist/v3/BaseContractShim.d.ts.map +1 -0
  133. package/dist/v3/BaseContractShim.js +185 -0
  134. package/dist/v3/BaseContractShim.js.map +1 -0
  135. package/dist/v3/BaseRegistry.d.ts +30 -0
  136. package/dist/v3/BaseRegistry.d.ts.map +1 -0
  137. package/dist/v3/BaseRegistry.js +64 -0
  138. package/dist/v3/BaseRegistry.js.map +1 -0
  139. package/dist/v3/EIP-712.d.ts +35 -0
  140. package/dist/v3/EIP-712.d.ts.map +1 -0
  141. package/dist/v3/EIP-712.js +57 -0
  142. package/dist/v3/EIP-712.js.map +1 -0
  143. package/dist/v3/IBanningShim.d.ts +7 -0
  144. package/dist/v3/IBanningShim.d.ts.map +1 -0
  145. package/dist/v3/IBanningShim.js +8 -0
  146. package/dist/v3/IBanningShim.js.map +1 -0
  147. package/dist/v3/IChannelShim.d.ts +8 -0
  148. package/dist/v3/IChannelShim.d.ts.map +1 -0
  149. package/dist/v3/IChannelShim.js +8 -0
  150. package/dist/v3/IChannelShim.js.map +1 -0
  151. package/dist/v3/ICreateSpaceShim.d.ts +9 -0
  152. package/dist/v3/ICreateSpaceShim.d.ts.map +1 -0
  153. package/dist/v3/ICreateSpaceShim.js +8 -0
  154. package/dist/v3/ICreateSpaceShim.js.map +1 -0
  155. package/dist/v3/IDropFacetShim.d.ts +7 -0
  156. package/dist/v3/IDropFacetShim.d.ts.map +1 -0
  157. package/dist/v3/IDropFacetShim.js +8 -0
  158. package/dist/v3/IDropFacetShim.js.map +1 -0
  159. package/dist/v3/IERC721AQueryableShim.d.ts +7 -0
  160. package/dist/v3/IERC721AQueryableShim.d.ts.map +1 -0
  161. package/dist/v3/IERC721AQueryableShim.js +8 -0
  162. package/dist/v3/IERC721AQueryableShim.js.map +1 -0
  163. package/dist/v3/IERC721AShim.d.ts +7 -0
  164. package/dist/v3/IERC721AShim.d.ts.map +1 -0
  165. package/dist/v3/IERC721AShim.js +8 -0
  166. package/dist/v3/IERC721AShim.js.map +1 -0
  167. package/dist/v3/IEntitlementCheckerShim.d.ts +7 -0
  168. package/dist/v3/IEntitlementCheckerShim.d.ts.map +1 -0
  169. package/dist/v3/IEntitlementCheckerShim.js +8 -0
  170. package/dist/v3/IEntitlementCheckerShim.js.map +1 -0
  171. package/dist/v3/IEntitlementDataQueryableShim.d.ts +8 -0
  172. package/dist/v3/IEntitlementDataQueryableShim.d.ts.map +1 -0
  173. package/dist/v3/IEntitlementDataQueryableShim.js +8 -0
  174. package/dist/v3/IEntitlementDataQueryableShim.js.map +1 -0
  175. package/dist/v3/IEntitlementsShim.d.ts +8 -0
  176. package/dist/v3/IEntitlementsShim.d.ts.map +1 -0
  177. package/dist/v3/IEntitlementsShim.js +8 -0
  178. package/dist/v3/IEntitlementsShim.js.map +1 -0
  179. package/dist/v3/ILegacySpaceArchitectShim.d.ts +8 -0
  180. package/dist/v3/ILegacySpaceArchitectShim.d.ts.map +1 -0
  181. package/dist/v3/ILegacySpaceArchitectShim.js +8 -0
  182. package/dist/v3/ILegacySpaceArchitectShim.js.map +1 -0
  183. package/dist/v3/IMembershipMetadataShim.d.ts +7 -0
  184. package/dist/v3/IMembershipMetadataShim.d.ts.map +1 -0
  185. package/dist/v3/IMembershipMetadataShim.js +8 -0
  186. package/dist/v3/IMembershipMetadataShim.js.map +1 -0
  187. package/dist/v3/IMembershipShim.d.ts +18 -0
  188. package/dist/v3/IMembershipShim.d.ts.map +1 -0
  189. package/dist/v3/IMembershipShim.js +73 -0
  190. package/dist/v3/IMembershipShim.js.map +1 -0
  191. package/dist/v3/IMulticallShim.d.ts +7 -0
  192. package/dist/v3/IMulticallShim.d.ts.map +1 -0
  193. package/dist/v3/IMulticallShim.js +8 -0
  194. package/dist/v3/IMulticallShim.js.map +1 -0
  195. package/dist/v3/INodeOperatorShim.d.ts +7 -0
  196. package/dist/v3/INodeOperatorShim.d.ts.map +1 -0
  197. package/dist/v3/INodeOperatorShim.js +8 -0
  198. package/dist/v3/INodeOperatorShim.js.map +1 -0
  199. package/dist/v3/INodeRegistryShim.d.ts +7 -0
  200. package/dist/v3/INodeRegistryShim.d.ts.map +1 -0
  201. package/dist/v3/INodeRegistryShim.js +8 -0
  202. package/dist/v3/INodeRegistryShim.js.map +1 -0
  203. package/dist/v3/IOperatorRegistryShim.d.ts +7 -0
  204. package/dist/v3/IOperatorRegistryShim.d.ts.map +1 -0
  205. package/dist/v3/IOperatorRegistryShim.js +8 -0
  206. package/dist/v3/IOperatorRegistryShim.js.map +1 -0
  207. package/dist/v3/IPrepayShim.d.ts +7 -0
  208. package/dist/v3/IPrepayShim.d.ts.map +1 -0
  209. package/dist/v3/IPrepayShim.js +8 -0
  210. package/dist/v3/IPrepayShim.js.map +1 -0
  211. package/dist/v3/IPricingShim.d.ts +8 -0
  212. package/dist/v3/IPricingShim.d.ts.map +1 -0
  213. package/dist/v3/IPricingShim.js +8 -0
  214. package/dist/v3/IPricingShim.js.map +1 -0
  215. package/dist/v3/IReviewShim.d.ts +61 -0
  216. package/dist/v3/IReviewShim.d.ts.map +1 -0
  217. package/dist/v3/IReviewShim.js +101 -0
  218. package/dist/v3/IReviewShim.js.map +1 -0
  219. package/dist/v3/IRiverPointsShim.d.ts +7 -0
  220. package/dist/v3/IRiverPointsShim.d.ts.map +1 -0
  221. package/dist/v3/IRiverPointsShim.js +8 -0
  222. package/dist/v3/IRiverPointsShim.js.map +1 -0
  223. package/dist/v3/IRolesShim.d.ts +8 -0
  224. package/dist/v3/IRolesShim.d.ts.map +1 -0
  225. package/dist/v3/IRolesShim.js +8 -0
  226. package/dist/v3/IRolesShim.js.map +1 -0
  227. package/dist/v3/IRuleEntitlementShim.d.ts +6 -0
  228. package/dist/v3/IRuleEntitlementShim.d.ts.map +1 -0
  229. package/dist/v3/IRuleEntitlementShim.js +3 -0
  230. package/dist/v3/IRuleEntitlementShim.js.map +1 -0
  231. package/dist/v3/IRuleEntitlementV2Shim.d.ts +6 -0
  232. package/dist/v3/IRuleEntitlementV2Shim.d.ts.map +1 -0
  233. package/dist/v3/IRuleEntitlementV2Shim.js +3 -0
  234. package/dist/v3/IRuleEntitlementV2Shim.js.map +1 -0
  235. package/dist/v3/ISpaceArchitectShim.d.ts +8 -0
  236. package/dist/v3/ISpaceArchitectShim.d.ts.map +1 -0
  237. package/dist/v3/ISpaceArchitectShim.js +38 -0
  238. package/dist/v3/ISpaceArchitectShim.js.map +1 -0
  239. package/dist/v3/ISpaceDelegationShim.d.ts +7 -0
  240. package/dist/v3/ISpaceDelegationShim.d.ts.map +1 -0
  241. package/dist/v3/ISpaceDelegationShim.js +8 -0
  242. package/dist/v3/ISpaceDelegationShim.js.map +1 -0
  243. package/dist/v3/ISpaceOwnerShim.d.ts +8 -0
  244. package/dist/v3/ISpaceOwnerShim.d.ts.map +1 -0
  245. package/dist/v3/ISpaceOwnerShim.js +8 -0
  246. package/dist/v3/ISpaceOwnerShim.js.map +1 -0
  247. package/dist/v3/IStreamRegistryShim.d.ts +7 -0
  248. package/dist/v3/IStreamRegistryShim.d.ts.map +1 -0
  249. package/dist/v3/IStreamRegistryShim.js +8 -0
  250. package/dist/v3/IStreamRegistryShim.js.map +1 -0
  251. package/dist/v3/ITippingShim.d.ts +20 -0
  252. package/dist/v3/ITippingShim.d.ts.map +1 -0
  253. package/dist/v3/ITippingShim.js +46 -0
  254. package/dist/v3/ITippingShim.js.map +1 -0
  255. package/dist/v3/ITreasuryShim.d.ts +7 -0
  256. package/dist/v3/ITreasuryShim.d.ts.map +1 -0
  257. package/dist/v3/ITreasuryShim.js +8 -0
  258. package/dist/v3/ITreasuryShim.js.map +1 -0
  259. package/dist/v3/MockERC721AShim.d.ts +7 -0
  260. package/dist/v3/MockERC721AShim.d.ts.map +1 -0
  261. package/dist/v3/MockERC721AShim.js +8 -0
  262. package/dist/v3/MockERC721AShim.js.map +1 -0
  263. package/dist/v3/OwnableFacetShim.d.ts +7 -0
  264. package/dist/v3/OwnableFacetShim.d.ts.map +1 -0
  265. package/dist/v3/OwnableFacetShim.js +8 -0
  266. package/dist/v3/OwnableFacetShim.js.map +1 -0
  267. package/dist/v3/PlatformRequirements.d.ts +10 -0
  268. package/dist/v3/PlatformRequirements.d.ts.map +1 -0
  269. package/dist/v3/PlatformRequirements.js +17 -0
  270. package/dist/v3/PlatformRequirements.js.map +1 -0
  271. package/dist/v3/PricingModules.d.ts +13 -0
  272. package/dist/v3/PricingModules.d.ts.map +1 -0
  273. package/dist/v3/PricingModules.js +23 -0
  274. package/dist/v3/PricingModules.js.map +1 -0
  275. package/dist/v3/RiverAirdropDapp.d.ts +16 -0
  276. package/dist/v3/RiverAirdropDapp.d.ts.map +1 -0
  277. package/dist/v3/RiverAirdropDapp.js +30 -0
  278. package/dist/v3/RiverAirdropDapp.js.map +1 -0
  279. package/dist/v3/RiverRegistry.d.ts +32 -0
  280. package/dist/v3/RiverRegistry.d.ts.map +1 -0
  281. package/dist/v3/RiverRegistry.js +96 -0
  282. package/dist/v3/RiverRegistry.js.map +1 -0
  283. package/dist/v3/RuleEntitlementShim.d.ts +11 -0
  284. package/dist/v3/RuleEntitlementShim.d.ts.map +1 -0
  285. package/dist/v3/RuleEntitlementShim.js +38 -0
  286. package/dist/v3/RuleEntitlementShim.js.map +1 -0
  287. package/dist/v3/RuleEntitlementV2Shim.d.ts +11 -0
  288. package/dist/v3/RuleEntitlementV2Shim.d.ts.map +1 -0
  289. package/dist/v3/RuleEntitlementV2Shim.js +38 -0
  290. package/dist/v3/RuleEntitlementV2Shim.js.map +1 -0
  291. package/dist/v3/Space.d.ts +97 -0
  292. package/dist/v3/Space.d.ts.map +1 -0
  293. package/dist/v3/Space.js +440 -0
  294. package/dist/v3/Space.js.map +1 -0
  295. package/dist/v3/SpaceDapp.d.ts +212 -0
  296. package/dist/v3/SpaceDapp.d.ts.map +1 -0
  297. package/dist/v3/SpaceDapp.js +1164 -0
  298. package/dist/v3/SpaceDapp.js.map +1 -0
  299. package/dist/v3/SpaceOwner.d.ts +13 -0
  300. package/dist/v3/SpaceOwner.d.ts.map +1 -0
  301. package/dist/v3/SpaceOwner.js +18 -0
  302. package/dist/v3/SpaceOwner.js.map +1 -0
  303. package/dist/v3/SpaceRegistrar.d.ts +25 -0
  304. package/dist/v3/SpaceRegistrar.d.ts.map +1 -0
  305. package/dist/v3/SpaceRegistrar.js +56 -0
  306. package/dist/v3/SpaceRegistrar.js.map +1 -0
  307. package/dist/v3/TokenPausableFacetShim.d.ts +7 -0
  308. package/dist/v3/TokenPausableFacetShim.d.ts.map +1 -0
  309. package/dist/v3/TokenPausableFacetShim.js +8 -0
  310. package/dist/v3/TokenPausableFacetShim.js.map +1 -0
  311. package/dist/v3/UserEntitlementShim.d.ts +11 -0
  312. package/dist/v3/UserEntitlementShim.d.ts.map +1 -0
  313. package/dist/v3/UserEntitlementShim.js +44 -0
  314. package/dist/v3/UserEntitlementShim.js.map +1 -0
  315. package/dist/v3/WalletLink.d.ts +50 -0
  316. package/dist/v3/WalletLink.d.ts.map +1 -0
  317. package/dist/v3/WalletLink.js +215 -0
  318. package/dist/v3/WalletLink.js.map +1 -0
  319. package/dist/v3/WalletLinkShim.d.ts +7 -0
  320. package/dist/v3/WalletLinkShim.d.ts.map +1 -0
  321. package/dist/v3/WalletLinkShim.js +8 -0
  322. package/dist/v3/WalletLinkShim.js.map +1 -0
  323. package/dist/v3/index.d.ts +24 -0
  324. package/dist/v3/index.d.ts.map +1 -0
  325. package/dist/v3/index.js +24 -0
  326. package/dist/v3/index.js.map +1 -0
  327. package/package.json +54 -0
@@ -0,0 +1,1164 @@
1
+ import { EntitlementModuleType, isPermission, isUpdateChannelStatusParams, Permission, } from '../ContractTypes';
2
+ import { computeDelegatorsForProvider } from '../DelegateRegistry';
3
+ import { ethers } from 'ethers';
4
+ import { LOCALHOST_CHAIN_ID } from '../Web3Constants';
5
+ import { SpaceRegistrar } from './SpaceRegistrar';
6
+ import { createEntitlementStruct, createLegacyEntitlementStruct } from '../ConvertersRoles';
7
+ import { convertRuleDataV1ToV2 } from '../ConvertersEntitlements';
8
+ import { WalletLink, INVALID_ADDRESS } from './WalletLink';
9
+ import { RiverAirdropDapp, UNKNOWN_ERROR, } from './index';
10
+ import { PricingModules } from './PricingModules';
11
+ import { dlogger, isTestEnv } from '@towns-protocol/dlog';
12
+ import { EVERYONE_ADDRESS, stringifyChannelMetadataJSON, NoEntitledWalletError } from '../Utils';
13
+ import { evaluateOperationsForEntitledWallet, ruleDataToOperations, findEthereumProviders, } from '../entitlement';
14
+ import { PlatformRequirements } from './PlatformRequirements';
15
+ import { EntitlementCache } from '../EntitlementCache';
16
+ const logger = dlogger('csb:SpaceDapp:debug');
17
+ class EntitlementDataCacheResult {
18
+ value;
19
+ cacheHit;
20
+ isPositive;
21
+ constructor(value) {
22
+ this.value = value;
23
+ this.cacheHit = false;
24
+ this.isPositive = true;
25
+ }
26
+ }
27
+ class EntitledWalletCacheResult {
28
+ value;
29
+ cacheHit;
30
+ isPositive;
31
+ constructor(value) {
32
+ this.value = value;
33
+ this.cacheHit = false;
34
+ this.isPositive = value !== undefined;
35
+ }
36
+ }
37
+ class BooleanCacheResult {
38
+ value;
39
+ cacheHit;
40
+ isPositive;
41
+ constructor(value) {
42
+ this.value = value;
43
+ this.cacheHit = false;
44
+ this.isPositive = value;
45
+ }
46
+ }
47
+ class EntitlementRequest {
48
+ spaceId;
49
+ channelId;
50
+ userId;
51
+ permission;
52
+ constructor(spaceId, channelId, userId, permission) {
53
+ this.spaceId = spaceId;
54
+ this.channelId = channelId;
55
+ this.userId = userId;
56
+ this.permission = permission;
57
+ }
58
+ toKey() {
59
+ return `{spaceId:${this.spaceId},channelId:${this.channelId},userId:${this.userId},permission:${this.permission}}`;
60
+ }
61
+ }
62
+ function newSpaceEntitlementEvaluationRequest(spaceId, userId, permission) {
63
+ return new EntitlementRequest(spaceId, '', userId, permission);
64
+ }
65
+ function newChannelEntitlementEvaluationRequest(spaceId, channelId, userId, permission) {
66
+ return new EntitlementRequest(spaceId, channelId, userId, permission);
67
+ }
68
+ function newSpaceEntitlementRequest(spaceId, permission) {
69
+ return new EntitlementRequest(spaceId, '', '', permission);
70
+ }
71
+ function newChannelEntitlementRequest(spaceId, channelId, permission) {
72
+ return new EntitlementRequest(spaceId, channelId, '', permission);
73
+ }
74
+ function ensureHexPrefix(value) {
75
+ return value.startsWith('0x') ? value : `0x${value}`;
76
+ }
77
+ const EmptyXchainConfig = {
78
+ supportedRpcUrls: {},
79
+ etherNativeNetworkIds: [],
80
+ ethereumNetworkIds: [],
81
+ };
82
+ export class SpaceDapp {
83
+ isLegacySpaceCache;
84
+ config;
85
+ provider;
86
+ spaceRegistrar;
87
+ pricingModules;
88
+ walletLink;
89
+ platformRequirements;
90
+ airdrop;
91
+ entitlementCache;
92
+ entitledWalletCache;
93
+ entitlementEvaluationCache;
94
+ constructor(config, provider) {
95
+ this.isLegacySpaceCache = new Map();
96
+ this.config = config;
97
+ this.provider = provider;
98
+ this.spaceRegistrar = new SpaceRegistrar(config, provider);
99
+ this.walletLink = new WalletLink(config, provider);
100
+ this.pricingModules = new PricingModules(config, provider);
101
+ this.platformRequirements = new PlatformRequirements(config.addresses.spaceFactory, provider);
102
+ this.airdrop = new RiverAirdropDapp(config, provider);
103
+ // For RPC providers that pool for events, we need to set the polling interval to a lower value
104
+ // so that we don't miss events that may be emitted in between polling intervals. The Ethers
105
+ // default is 4000ms, which is based on the assumption of 12s mainnet blocktimes.
106
+ if ('pollingInterval' in provider && typeof provider.pollingInterval === 'number') {
107
+ provider.pollingInterval = 250;
108
+ }
109
+ const isLocalDev = isTestEnv() || config.chainId === LOCALHOST_CHAIN_ID;
110
+ const cacheOpts = {
111
+ positiveCacheTTLSeconds: isLocalDev ? 5 : 15 * 60,
112
+ negativeCacheTTLSeconds: 2,
113
+ };
114
+ this.entitlementCache = new EntitlementCache(cacheOpts);
115
+ this.entitledWalletCache = new EntitlementCache(cacheOpts);
116
+ this.entitlementEvaluationCache = new EntitlementCache(cacheOpts);
117
+ }
118
+ async isLegacySpace(spaceId) {
119
+ const cachedValue = this.isLegacySpaceCache.get(spaceId);
120
+ if (cachedValue !== undefined) {
121
+ return cachedValue;
122
+ }
123
+ const space = this.getSpace(spaceId);
124
+ if (!space) {
125
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
126
+ }
127
+ // Legacy spaces do not have RuleEntitlementV2
128
+ const maybeShim = await space.findEntitlementByType(EntitlementModuleType.RuleEntitlementV2);
129
+ const isLegacy = maybeShim === null;
130
+ this.isLegacySpaceCache.set(spaceId, isLegacy);
131
+ return isLegacy;
132
+ }
133
+ async addRoleToChannel(spaceId, channelNetworkId, roleId, signer, txnOpts) {
134
+ const space = this.getSpace(spaceId);
135
+ if (!space) {
136
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
137
+ }
138
+ const channelId = ensureHexPrefix(channelNetworkId);
139
+ return wrapTransaction(() => space.Channels.write(signer).addRoleToChannel(channelId, roleId), txnOpts);
140
+ }
141
+ async waitForRoleCreated(spaceId, txn) {
142
+ const receipt = await this.provider.waitForTransaction(txn.hash);
143
+ if (receipt.status === 0) {
144
+ return { roleId: undefined, error: new Error('Transaction failed') };
145
+ }
146
+ const parsedLogs = await this.parseSpaceLogs(spaceId, receipt.logs);
147
+ const roleCreatedEvent = parsedLogs.find((log) => log?.name === 'RoleCreated');
148
+ if (!roleCreatedEvent) {
149
+ return { roleId: undefined, error: new Error('RoleCreated event not found') };
150
+ }
151
+ const roleId = roleCreatedEvent.args[1].toNumber();
152
+ return { roleId, error: undefined };
153
+ }
154
+ async banWalletAddress(spaceId, walletAddress, signer, txnOpts) {
155
+ const space = this.getSpace(spaceId);
156
+ if (!space) {
157
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
158
+ }
159
+ const token = await space.ERC721AQueryable.read
160
+ .tokensOfOwner(walletAddress)
161
+ .then((tokens) => tokens[0]);
162
+ return wrapTransaction(() => space.Banning.write(signer).ban(token), txnOpts);
163
+ }
164
+ async unbanWalletAddress(spaceId, walletAddress, signer, txnOpts) {
165
+ const space = this.getSpace(spaceId);
166
+ if (!space) {
167
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
168
+ }
169
+ const token = await space.ERC721AQueryable.read
170
+ .tokensOfOwner(walletAddress)
171
+ .then((tokens) => tokens[0]);
172
+ return wrapTransaction(() => space.Banning.write(signer).unban(token), txnOpts);
173
+ }
174
+ async walletAddressIsBanned(spaceId, walletAddress) {
175
+ const space = this.getSpace(spaceId);
176
+ if (!space) {
177
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
178
+ }
179
+ const token = await space.ERC721AQueryable.read
180
+ .tokensOfOwner(walletAddress)
181
+ .then((tokens) => tokens[0]);
182
+ return await space.Banning.read.isBanned(token);
183
+ }
184
+ async bannedWalletAddresses(spaceId) {
185
+ const space = this.getSpace(spaceId);
186
+ if (!space) {
187
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
188
+ }
189
+ const bannedTokenIds = await space.Banning.read.banned();
190
+ const bannedWalletAddresses = await Promise.all(bannedTokenIds.map(async (tokenId) => await space.ERC721A.read.ownerOf(tokenId)));
191
+ return bannedWalletAddresses;
192
+ }
193
+ async createLegacySpace(params, signer, txnOpts) {
194
+ const spaceInfo = {
195
+ name: params.spaceName,
196
+ uri: params.uri,
197
+ membership: params.membership,
198
+ channel: {
199
+ metadata: params.channelName || '',
200
+ },
201
+ shortDescription: params.shortDescription ?? '',
202
+ longDescription: params.longDescription ?? '',
203
+ };
204
+ return wrapTransaction(() => this.spaceRegistrar.LegacySpaceArchitect.write(signer).createSpace(spaceInfo), txnOpts);
205
+ }
206
+ async createSpace(params, signer, txnOpts) {
207
+ return wrapTransaction(() => {
208
+ const createSpaceFunction = this.spaceRegistrar.CreateSpace.write(signer)['createSpaceWithPrepay(((string,string,string,string),((string,string,uint256,uint256,uint64,address,address,uint256,address),(bool,address[],bytes,bool),string[]),(string),(uint256)))'];
209
+ return createSpaceFunction({
210
+ channel: {
211
+ metadata: params.channelName || '',
212
+ },
213
+ membership: params.membership,
214
+ metadata: {
215
+ name: params.spaceName,
216
+ uri: params.uri,
217
+ longDescription: params.longDescription || '',
218
+ shortDescription: params.shortDescription || '',
219
+ },
220
+ prepay: {
221
+ supply: params.prepaySupply ?? 0,
222
+ },
223
+ });
224
+ }, txnOpts);
225
+ }
226
+ async createChannel(spaceId, channelName, channelDescription, channelNetworkId, roleIds, signer, txnOpts) {
227
+ const space = this.getSpace(spaceId);
228
+ if (!space) {
229
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
230
+ }
231
+ const channelId = ensureHexPrefix(channelNetworkId);
232
+ return wrapTransaction(() => space.Channels.write(signer).createChannel(channelId, stringifyChannelMetadataJSON({
233
+ name: channelName,
234
+ description: channelDescription,
235
+ }), roleIds), txnOpts);
236
+ }
237
+ async createChannelWithPermissionOverrides(spaceId, channelName, channelDescription, channelNetworkId, roles, signer, txnOpts) {
238
+ const space = this.getSpace(spaceId);
239
+ if (!space) {
240
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
241
+ }
242
+ const channelId = ensureHexPrefix(channelNetworkId);
243
+ return wrapTransaction(() => space.Channels.write(signer).createChannelWithOverridePermissions(channelId, stringifyChannelMetadataJSON({
244
+ name: channelName,
245
+ description: channelDescription,
246
+ }), roles), txnOpts);
247
+ }
248
+ async legacyCreateRole(spaceId, roleName, permissions, users, ruleData, signer, txnOpts) {
249
+ const space = this.getSpace(spaceId);
250
+ if (!space) {
251
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
252
+ }
253
+ const entitlements = await createLegacyEntitlementStruct(space, users, ruleData);
254
+ return wrapTransaction(() => space.Roles.write(signer).createRole(roleName, permissions, entitlements), txnOpts);
255
+ }
256
+ async createRole(spaceId, roleName, permissions, users, ruleData, signer, txnOpts) {
257
+ const space = this.getSpace(spaceId);
258
+ if (!space) {
259
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
260
+ }
261
+ const entitlements = await createEntitlementStruct(space, users, ruleData);
262
+ return wrapTransaction(() => space.Roles.write(signer).createRole(roleName, permissions, entitlements), txnOpts);
263
+ }
264
+ async deleteRole(spaceId, roleId, signer, txnOpts) {
265
+ const space = this.getSpace(spaceId);
266
+ if (!space) {
267
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
268
+ }
269
+ return wrapTransaction(() => space.Roles.write(signer).removeRole(roleId), txnOpts);
270
+ }
271
+ async getChannels(spaceId) {
272
+ const space = this.getSpace(spaceId);
273
+ if (!space) {
274
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
275
+ }
276
+ return space.getChannels();
277
+ }
278
+ async tokenURI(spaceId) {
279
+ const space = this.getSpace(spaceId);
280
+ if (!space) {
281
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
282
+ }
283
+ const spaceInfo = await space.getSpaceInfo();
284
+ return space.SpaceOwnerErc721A.read.tokenURI(spaceInfo.tokenId);
285
+ }
286
+ memberTokenURI(spaceId, tokenId) {
287
+ const space = this.getSpace(spaceId);
288
+ if (!space) {
289
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
290
+ }
291
+ return space.ERC721A.read.tokenURI(tokenId);
292
+ }
293
+ async getChannelDetails(spaceId, channelNetworkId) {
294
+ const space = this.getSpace(spaceId);
295
+ if (!space) {
296
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
297
+ }
298
+ const channelId = ensureHexPrefix(channelNetworkId);
299
+ return space.getChannel(channelId);
300
+ }
301
+ async getPermissionsByRoleId(spaceId, roleId) {
302
+ const space = this.getSpace(spaceId);
303
+ if (!space) {
304
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
305
+ }
306
+ return space.getPermissionsByRoleId(roleId);
307
+ }
308
+ async getRole(spaceId, roleId) {
309
+ const space = this.getSpace(spaceId);
310
+ if (!space) {
311
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
312
+ }
313
+ return space.getRole(roleId);
314
+ }
315
+ async getRoles(spaceId) {
316
+ const space = this.getSpace(spaceId);
317
+ if (!space) {
318
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
319
+ }
320
+ const roles = await space.Roles.read.getRoles();
321
+ return roles.map((role) => ({
322
+ roleId: role.id.toNumber(),
323
+ name: role.name,
324
+ }));
325
+ }
326
+ async getSpaceInfo(spaceId) {
327
+ const space = this.getSpace(spaceId);
328
+ if (!space) {
329
+ return undefined;
330
+ }
331
+ const [owner, disabled, spaceInfo] = await Promise.all([
332
+ space.Ownable.read.owner(),
333
+ space.Pausable.read.paused(),
334
+ space.getSpaceInfo(),
335
+ ]);
336
+ return {
337
+ address: space.Address,
338
+ networkId: space.SpaceId,
339
+ name: spaceInfo.name ?? '',
340
+ owner,
341
+ disabled,
342
+ uri: spaceInfo.uri ?? '',
343
+ tokenId: ethers.BigNumber.from(spaceInfo.tokenId).toString(),
344
+ createdAt: ethers.BigNumber.from(spaceInfo.createdAt).toString(),
345
+ shortDescription: spaceInfo.shortDescription ?? '',
346
+ longDescription: spaceInfo.longDescription ?? '',
347
+ };
348
+ }
349
+ async updateSpaceInfo(spaceId, name, uri, shortDescription, longDescription, signer, txnOpts) {
350
+ const space = this.getSpace(spaceId);
351
+ if (!space) {
352
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
353
+ }
354
+ return wrapTransaction(() => space.SpaceOwner.write(signer).updateSpaceInfo(space.Address, name, uri, shortDescription, longDescription), txnOpts);
355
+ }
356
+ async decodeEntitlementData(space, entitlementData) {
357
+ const entitlements = entitlementData.map((x) => ({
358
+ entitlementType: x.entitlementType,
359
+ ruleEntitlement: undefined,
360
+ userEntitlement: undefined,
361
+ }));
362
+ const [userEntitlementShim, ruleEntitlementShim, ruleEntitlementV2Shim] = (await Promise.all([
363
+ space.findEntitlementByType(EntitlementModuleType.UserEntitlement),
364
+ space.findEntitlementByType(EntitlementModuleType.RuleEntitlement),
365
+ space.findEntitlementByType(EntitlementModuleType.RuleEntitlementV2),
366
+ ]));
367
+ for (let i = 0; i < entitlementData.length; i++) {
368
+ const entitlement = entitlementData[i];
369
+ if (entitlement.entitlementType ===
370
+ EntitlementModuleType.RuleEntitlement) {
371
+ entitlements[i].entitlementType = EntitlementModuleType.RuleEntitlement;
372
+ const decodedData = ruleEntitlementShim?.decodeGetRuleData(entitlement.entitlementData);
373
+ if (decodedData) {
374
+ entitlements[i].ruleEntitlement = {
375
+ kind: 'v1',
376
+ rules: decodedData,
377
+ };
378
+ }
379
+ }
380
+ else if (entitlement.entitlementType ===
381
+ EntitlementModuleType.RuleEntitlementV2) {
382
+ entitlements[i].entitlementType = EntitlementModuleType.RuleEntitlementV2;
383
+ const decodedData = ruleEntitlementV2Shim?.decodeGetRuleData(entitlement.entitlementData);
384
+ if (decodedData) {
385
+ entitlements[i].ruleEntitlement = {
386
+ kind: 'v2',
387
+ rules: decodedData,
388
+ };
389
+ }
390
+ }
391
+ else if (entitlement.entitlementType ===
392
+ EntitlementModuleType.UserEntitlement) {
393
+ entitlements[i].entitlementType = EntitlementModuleType.UserEntitlement;
394
+ const decodedData = userEntitlementShim?.decodeGetAddresses(entitlement.entitlementData);
395
+ if (decodedData) {
396
+ entitlements[i].userEntitlement = decodedData;
397
+ }
398
+ }
399
+ else {
400
+ throw new Error('Unknown entitlement type');
401
+ }
402
+ }
403
+ return entitlements;
404
+ }
405
+ async getEntitlementsForPermission(spaceId, permission) {
406
+ const { value } = await this.entitlementCache.executeUsingCache(newSpaceEntitlementRequest(spaceId, permission), async (request) => {
407
+ const entitlementData = await this.getEntitlementsForPermissionUncached(request.spaceId, request.permission);
408
+ return new EntitlementDataCacheResult(entitlementData);
409
+ });
410
+ return value;
411
+ }
412
+ async getEntitlementsForPermissionUncached(spaceId, permission) {
413
+ const space = this.getSpace(spaceId);
414
+ if (!space) {
415
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
416
+ }
417
+ const entitlementData = await space.EntitlementDataQueryable.read.getEntitlementDataByPermission(permission);
418
+ return await this.decodeEntitlementData(space, entitlementData);
419
+ }
420
+ async getChannelEntitlementsForPermission(spaceId, channelId, permission) {
421
+ const { value } = await this.entitlementCache.executeUsingCache(newChannelEntitlementRequest(spaceId, channelId, permission), async (request) => {
422
+ const entitlementData = await this.getChannelEntitlementsForPermissionUncached(request.spaceId, request.channelId, request.permission);
423
+ return new EntitlementDataCacheResult(entitlementData);
424
+ });
425
+ return value;
426
+ }
427
+ async getChannelEntitlementsForPermissionUncached(spaceId, channelId, permission) {
428
+ const space = this.getSpace(spaceId);
429
+ if (!space) {
430
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
431
+ }
432
+ const entitlementData = await space.EntitlementDataQueryable.read.getChannelEntitlementDataByPermission(channelId, permission);
433
+ return await this.decodeEntitlementData(space, entitlementData);
434
+ }
435
+ async getLinkedWallets(wallet) {
436
+ let linkedWallets = await this.walletLink.getLinkedWallets(wallet);
437
+ // If there are no linked wallets, consider that the wallet may be linked to another root key.
438
+ if (linkedWallets.length === 0) {
439
+ const possibleRoot = await this.walletLink.getRootKeyForWallet(wallet);
440
+ if (possibleRoot !== INVALID_ADDRESS) {
441
+ linkedWallets = await this.walletLink.getLinkedWallets(possibleRoot);
442
+ return [possibleRoot, ...linkedWallets];
443
+ }
444
+ }
445
+ return [wallet, ...linkedWallets];
446
+ }
447
+ async getMainnetDelegationsForLinkedWallets(linkedWallets, config) {
448
+ const delegatorSet = new Set();
449
+ const ethProviders = await findEthereumProviders(config);
450
+ for (const provider of ethProviders) {
451
+ const delegators = await computeDelegatorsForProvider(provider, linkedWallets);
452
+ for (const delegator of delegators) {
453
+ delegatorSet.add(delegator);
454
+ }
455
+ }
456
+ return delegatorSet;
457
+ }
458
+ async getLinkedWalletsWithDelegations(wallet, config) {
459
+ const linkedWallets = await this.getLinkedWallets(wallet);
460
+ const allWallets = new Set(linkedWallets);
461
+ const delegators = await this.getMainnetDelegationsForLinkedWallets(linkedWallets, config);
462
+ return [...new Set([...allWallets, ...delegators])];
463
+ }
464
+ async evaluateEntitledWallet(rootKey, allWallets, entitlements, xchainConfig) {
465
+ const isEveryOneSpace = entitlements.some((e) => e.userEntitlement?.includes(EVERYONE_ADDRESS));
466
+ if (isEveryOneSpace) {
467
+ return rootKey;
468
+ }
469
+ // Evaluate all user entitlements first, as they do not require external calls.
470
+ for (const entitlement of entitlements) {
471
+ for (const user of allWallets) {
472
+ if (entitlement.userEntitlement?.includes(user)) {
473
+ return user;
474
+ }
475
+ }
476
+ }
477
+ // Accumulate all RuleDataV1 entitlements and convert to V2s.
478
+ const ruleEntitlements = entitlements
479
+ .filter((x) => x.entitlementType === EntitlementModuleType.RuleEntitlement &&
480
+ x.ruleEntitlement?.kind == 'v1')
481
+ .map((x) => convertRuleDataV1ToV2(x.ruleEntitlement.rules));
482
+ // Add all RuleDataV2 entitlements.
483
+ ruleEntitlements.push(...entitlements
484
+ .filter((x) => x.entitlementType === EntitlementModuleType.RuleEntitlementV2 &&
485
+ x.ruleEntitlement?.kind == 'v2')
486
+ .map((x) => x.ruleEntitlement.rules));
487
+ return await Promise.any(ruleEntitlements.map(async (ruleData) => {
488
+ if (!ruleData) {
489
+ throw new Error('Rule data not found');
490
+ }
491
+ const operations = ruleDataToOperations(ruleData);
492
+ const result = await evaluateOperationsForEntitledWallet(operations, allWallets, xchainConfig);
493
+ if (result !== ethers.constants.AddressZero) {
494
+ return result;
495
+ }
496
+ // This is not a true error, but is used here so that the Promise.any will not
497
+ // resolve with an unentitled wallet.
498
+ throw new NoEntitledWalletError();
499
+ })).catch(NoEntitledWalletError.throwIfRuntimeErrors);
500
+ }
501
+ /**
502
+ * Checks if user has a wallet entitled to join a space based on the minter role rule entitlements
503
+ */
504
+ async getEntitledWalletForJoiningSpace(spaceId, rootKey, xchainConfig) {
505
+ const { value } = await this.entitledWalletCache.executeUsingCache(newSpaceEntitlementEvaluationRequest(spaceId, rootKey, Permission.JoinSpace), async (request) => {
506
+ const entitledWallet = await this.getEntitledWalletForJoiningSpaceUncached(request.spaceId, request.userId, xchainConfig);
507
+ return new EntitledWalletCacheResult(entitledWallet);
508
+ });
509
+ return value;
510
+ }
511
+ async getEntitledWalletForJoiningSpaceUncached(spaceId, rootKey, xchainConfig) {
512
+ const allWallets = await this.getLinkedWalletsWithDelegations(rootKey, xchainConfig);
513
+ const space = this.getSpace(spaceId);
514
+ if (!space) {
515
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
516
+ }
517
+ const owner = await space.Ownable.read.owner();
518
+ // Space owner is entitled to all channels
519
+ if (allWallets.includes(owner)) {
520
+ return owner;
521
+ }
522
+ const bannedWallets = await this.bannedWalletAddresses(spaceId);
523
+ for (const wallet of allWallets) {
524
+ if (bannedWallets.includes(wallet)) {
525
+ return;
526
+ }
527
+ }
528
+ const entitlements = await this.getEntitlementsForPermission(spaceId, Permission.JoinSpace);
529
+ return await this.evaluateEntitledWallet(rootKey, allWallets, entitlements, xchainConfig);
530
+ }
531
+ async isEntitledToSpace(spaceId, user, permission) {
532
+ const { value } = await this.entitlementEvaluationCache.executeUsingCache(newSpaceEntitlementEvaluationRequest(spaceId, user, permission), async (request) => {
533
+ const isEntitled = await this.isEntitledToSpaceUncached(request.spaceId, request.userId, request.permission);
534
+ return new BooleanCacheResult(isEntitled);
535
+ });
536
+ return value;
537
+ }
538
+ async isEntitledToSpaceUncached(spaceId, user, permission) {
539
+ const space = this.getSpace(spaceId);
540
+ if (!space) {
541
+ return false;
542
+ }
543
+ if (permission === Permission.JoinSpace) {
544
+ throw new Error('use getEntitledWalletForJoiningSpace instead of isEntitledToSpace');
545
+ }
546
+ return space.Entitlements.read.isEntitledToSpace(user, permission);
547
+ }
548
+ async isEntitledToChannel(spaceId, channelNetworkId, user, permission, xchainConfig = EmptyXchainConfig) {
549
+ const { value } = await this.entitlementEvaluationCache.executeUsingCache(newChannelEntitlementEvaluationRequest(spaceId, channelNetworkId, user, permission), async (request) => {
550
+ const isEntitled = await this.isEntitledToChannelUncached(request.spaceId, request.channelId, request.userId, request.permission, xchainConfig);
551
+ return new BooleanCacheResult(isEntitled);
552
+ });
553
+ return value;
554
+ }
555
+ async isEntitledToChannelUncached(spaceId, channelNetworkId, user, permission, xchainConfig) {
556
+ const space = this.getSpace(spaceId);
557
+ if (!space) {
558
+ return false;
559
+ }
560
+ const channelId = ensureHexPrefix(channelNetworkId);
561
+ const linkedWallets = await this.getLinkedWalletsWithDelegations(user, xchainConfig);
562
+ const owner = await space.Ownable.read.owner();
563
+ // Space owner is entitled to all channels
564
+ if (linkedWallets.includes(owner)) {
565
+ return true;
566
+ }
567
+ const bannedWallets = await this.bannedWalletAddresses(spaceId);
568
+ for (const wallet of linkedWallets) {
569
+ if (bannedWallets.includes(wallet)) {
570
+ return false;
571
+ }
572
+ }
573
+ const entitlements = await this.getChannelEntitlementsForPermission(spaceId, channelId, permission);
574
+ const entitledWallet = await this.evaluateEntitledWallet(user, linkedWallets, entitlements, xchainConfig);
575
+ return entitledWallet !== undefined;
576
+ }
577
+ parseSpaceFactoryError(error) {
578
+ if (!this.spaceRegistrar.SpaceArchitect) {
579
+ throw new Error('SpaceArchitect is not deployed properly.');
580
+ }
581
+ const decodedErr = this.spaceRegistrar.SpaceArchitect.parseError(error);
582
+ logger.error(decodedErr);
583
+ return decodedErr;
584
+ }
585
+ parseSpaceError(spaceId, error) {
586
+ const space = this.getSpace(spaceId);
587
+ if (!space) {
588
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
589
+ }
590
+ const decodedErr = space.parseError(error);
591
+ logger.error(decodedErr);
592
+ return decodedErr;
593
+ }
594
+ /**
595
+ * Attempts to parse an error against all contracts
596
+ * If you're error is not showing any data with this call, make sure the contract is listed either in parseSpaceError or nonSpaceContracts
597
+ * @param args
598
+ * @returns
599
+ */
600
+ parseAllContractErrors(args) {
601
+ let err;
602
+ if (args.spaceId) {
603
+ err = this.parseSpaceError(args.spaceId, args.error);
604
+ }
605
+ if (err && err?.name !== UNKNOWN_ERROR) {
606
+ return err;
607
+ }
608
+ err = this.spaceRegistrar.SpaceArchitect.parseError(args.error);
609
+ if (err?.name !== UNKNOWN_ERROR) {
610
+ return err;
611
+ }
612
+ const nonSpaceContracts = [this.pricingModules, this.walletLink];
613
+ for (const contract of nonSpaceContracts) {
614
+ err = contract.parseError(args.error);
615
+ if (err?.name !== UNKNOWN_ERROR) {
616
+ return err;
617
+ }
618
+ }
619
+ return err;
620
+ }
621
+ async parseSpaceLogs(spaceId, logs) {
622
+ const space = this.getSpace(spaceId);
623
+ if (!space) {
624
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
625
+ }
626
+ return logs.map((spaceLog) => {
627
+ try {
628
+ return space.parseLog(spaceLog);
629
+ }
630
+ catch (err) {
631
+ logger.error(err);
632
+ return;
633
+ }
634
+ });
635
+ }
636
+ async updateChannel(params, signer, txnOpts) {
637
+ const space = this.getSpace(params.spaceId);
638
+ if (!space) {
639
+ throw new Error(`Space with spaceId "${params.spaceId}" is not found.`);
640
+ }
641
+ const encodedCallData = await this.encodedUpdateChannelData(space, params);
642
+ return wrapTransaction(() => space.Multicall.write(signer).multicall(encodedCallData), txnOpts);
643
+ }
644
+ async encodedUpdateChannelData(space, params) {
645
+ const channelId = ensureHexPrefix(params.channelId);
646
+ if (isUpdateChannelStatusParams(params)) {
647
+ // When enabling or disabling channels, passing names and roles is not required.
648
+ // To ensure the contract accepts this exception, the metadata argument should be left empty.
649
+ return [
650
+ space.Channels.interface.encodeFunctionData('updateChannel', [channelId, '', true]),
651
+ ];
652
+ }
653
+ // data for the multicall
654
+ const encodedCallData = [];
655
+ // update the channel metadata
656
+ encodedCallData.push(space.Channels.interface.encodeFunctionData('updateChannel', [
657
+ channelId,
658
+ stringifyChannelMetadataJSON({
659
+ name: params.channelName,
660
+ description: params.channelDescription,
661
+ }),
662
+ params.disabled ?? false, // default to false
663
+ ]));
664
+ // update any channel role changes
665
+ const encodedUpdateChannelRoles = await this.encodeUpdateChannelRoles(space, params.channelId, params.roleIds);
666
+ for (const callData of encodedUpdateChannelRoles) {
667
+ encodedCallData.push(callData);
668
+ }
669
+ return encodedCallData;
670
+ }
671
+ async removeChannel(params, signer, txnOpts) {
672
+ const space = this.getSpace(params.spaceId);
673
+ if (!space) {
674
+ throw new Error(`Space with spaceId "${params.spaceId}" is not found.`);
675
+ }
676
+ return wrapTransaction(() => space.Channels.write(signer).removeChannel(params.channelId), txnOpts);
677
+ }
678
+ async legacyUpdateRole(params, signer, txnOpts) {
679
+ const space = this.getSpace(params.spaceNetworkId);
680
+ if (!space) {
681
+ throw new Error(`Space with spaceId "${params.spaceNetworkId}" is not found.`);
682
+ }
683
+ const updatedEntitlemets = await this.createLegacyUpdatedEntitlements(space, params);
684
+ return wrapTransaction(() => space.Roles.write(signer).updateRole(params.roleId, params.roleName, params.permissions, updatedEntitlemets), txnOpts);
685
+ }
686
+ async updateRole(params, signer, txnOpts) {
687
+ const space = this.getSpace(params.spaceNetworkId);
688
+ if (!space) {
689
+ throw new Error(`Space with spaceId "${params.spaceNetworkId}" is not found.`);
690
+ }
691
+ const updatedEntitlemets = await this.createUpdatedEntitlements(space, params);
692
+ return wrapTransaction(() => space.Roles.write(signer).updateRole(params.roleId, params.roleName, params.permissions, updatedEntitlemets), txnOpts);
693
+ }
694
+ async getChannelPermissionOverrides(spaceId, roleId, channelNetworkId) {
695
+ const space = this.getSpace(spaceId);
696
+ if (!space) {
697
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
698
+ }
699
+ const channelId = ensureHexPrefix(channelNetworkId);
700
+ return (await space.Roles.read.getChannelPermissionOverrides(roleId, channelId)).filter(isPermission);
701
+ }
702
+ async setChannelPermissionOverrides(params, signer, txnOpts) {
703
+ const space = this.getSpace(params.spaceNetworkId);
704
+ if (!space) {
705
+ throw new Error(`Space with spaceId "${params.spaceNetworkId}" is not found.`);
706
+ }
707
+ const channelId = ensureHexPrefix(params.channelId);
708
+ return wrapTransaction(() => space.Roles.write(signer).setChannelPermissionOverrides(params.roleId, channelId, params.permissions), txnOpts);
709
+ }
710
+ async clearChannelPermissionOverrides(params, signer, txnOpts) {
711
+ const space = this.getSpace(params.spaceNetworkId);
712
+ if (!space) {
713
+ throw new Error(`Space with spaceId "${params.spaceNetworkId}" is not found.`);
714
+ }
715
+ const channelId = ensureHexPrefix(params.channelId);
716
+ return wrapTransaction(() => space.Roles.write(signer).clearChannelPermissionOverrides(params.roleId, channelId), txnOpts);
717
+ }
718
+ async setSpaceAccess(spaceId, disabled, signer, txnOpts) {
719
+ const space = this.getSpace(spaceId);
720
+ if (!space) {
721
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
722
+ }
723
+ if (disabled) {
724
+ return wrapTransaction(() => space.Pausable.write(signer).pause(), txnOpts);
725
+ }
726
+ else {
727
+ return wrapTransaction(() => space.Pausable.write(signer).unpause(), txnOpts);
728
+ }
729
+ }
730
+ /**
731
+ *
732
+ * @param spaceId
733
+ * @param priceInWei
734
+ * @param signer
735
+ */
736
+ async setMembershipPrice(spaceId, priceInWei, signer, txnOpts) {
737
+ const space = this.getSpace(spaceId);
738
+ if (!space) {
739
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
740
+ }
741
+ return wrapTransaction(() => space.Membership.write(signer).setMembershipPrice(priceInWei), txnOpts);
742
+ }
743
+ async setMembershipPricingModule(spaceId, pricingModule, signer, txnOpts) {
744
+ const space = this.getSpace(spaceId);
745
+ if (!space) {
746
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
747
+ }
748
+ return wrapTransaction(() => space.Membership.write(signer).setMembershipPricingModule(pricingModule), txnOpts);
749
+ }
750
+ async setMembershipLimit(spaceId, limit, signer, txnOpts) {
751
+ const space = this.getSpace(spaceId);
752
+ if (!space) {
753
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
754
+ }
755
+ return wrapTransaction(() => space.Membership.write(signer).setMembershipLimit(limit), txnOpts);
756
+ }
757
+ async setMembershipFreeAllocation(spaceId, freeAllocation, signer, txnOpts) {
758
+ const space = this.getSpace(spaceId);
759
+ if (!space) {
760
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
761
+ }
762
+ return wrapTransaction(() => space.Membership.write(signer).setMembershipFreeAllocation(freeAllocation), txnOpts);
763
+ }
764
+ async prepayMembership(spaceId, supply, signer, txnOpts) {
765
+ const space = this.getSpace(spaceId);
766
+ if (!space) {
767
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
768
+ }
769
+ const cost = await space.Prepay.read.calculateMembershipPrepayFee(supply);
770
+ return wrapTransaction(() => space.Prepay.write(signer).prepayMembership(supply, {
771
+ value: cost,
772
+ }), txnOpts);
773
+ }
774
+ async getPrepaidMembershipSupply(spaceId) {
775
+ const space = this.getSpace(spaceId);
776
+ if (!space) {
777
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
778
+ }
779
+ return space.Prepay.read.prepaidMembershipSupply();
780
+ }
781
+ async setChannelAccess(spaceId, channelNetworkId, disabled, signer, txnOpts) {
782
+ const channelId = ensureHexPrefix(channelNetworkId);
783
+ const space = this.getSpace(spaceId);
784
+ if (!space) {
785
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
786
+ }
787
+ return wrapTransaction(() => space.Channels.write(signer).updateChannel(channelId, '', disabled), txnOpts);
788
+ }
789
+ async getSpaceMembershipTokenAddress(spaceId) {
790
+ const space = this.getSpace(spaceId);
791
+ if (!space) {
792
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
793
+ }
794
+ return space.Membership.address;
795
+ }
796
+ async getJoinSpacePriceDetails(spaceId) {
797
+ const space = this.getSpace(spaceId);
798
+ if (!space) {
799
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
800
+ }
801
+ // membershipPrice is either the maximum of either the price set during space creation, or the PlatformRequirements membership fee
802
+ // it will alawys be a value regardless of whether the space has free allocations or prepaid memberships
803
+ const membershipPrice = await space.Membership.read.getMembershipPrice();
804
+ // totalSupply = number of memberships minted
805
+ const totalSupply = await space.ERC721A.read.totalSupply();
806
+ // free allocation is set at space creation and is unchanging - it neither increases nor decreases
807
+ // if totalSupply < freeAllocation, the contracts won't charge for minting a membership nft,
808
+ // else it will charge the membershipPrice
809
+ const freeAllocation = await this.getMembershipFreeAllocation(spaceId);
810
+ // prepaidSupply = number of additional prepaid memberships
811
+ // if any prepaid memberships have been purchased, the contracts won't charge for minting a membership nft,
812
+ // else it will charge the membershipPrice
813
+ const prepaidSupply = await space.Prepay.read.prepaidMembershipSupply();
814
+ // remainingFreeSupply
815
+ // if totalSupply < freeAllocation, freeAllocation + prepaid - minted memberships
816
+ // else the remaining prepaidSupply if any
817
+ const remainingFreeSupply = totalSupply.lt(freeAllocation)
818
+ ? freeAllocation.add(prepaidSupply).sub(totalSupply)
819
+ : prepaidSupply;
820
+ return {
821
+ price: remainingFreeSupply.gt(0) ? ethers.BigNumber.from(0) : membershipPrice,
822
+ prepaidSupply,
823
+ remainingFreeSupply,
824
+ };
825
+ }
826
+ async getMembershipFreeAllocation(spaceId) {
827
+ const space = this.getSpace(spaceId);
828
+ if (!space) {
829
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
830
+ }
831
+ return space.Membership.read.getMembershipFreeAllocation();
832
+ }
833
+ async joinSpace(spaceId, recipient, signer, txnOpts) {
834
+ const joinSpaceStart = Date.now();
835
+ logger.log('joinSpace result before wrap', spaceId);
836
+ const getSpaceStart = Date.now();
837
+ const space = this.getSpace(spaceId);
838
+ if (!space) {
839
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
840
+ }
841
+ const issuedListener = space.Membership.listenForMembershipToken(recipient);
842
+ const blockNumber = await space.provider?.getBlockNumber();
843
+ logger.log('joinSpace before blockNumber', Date.now() - getSpaceStart, blockNumber);
844
+ const getPriceStart = Date.now();
845
+ const { price } = await this.getJoinSpacePriceDetails(spaceId);
846
+ logger.log('joinSpace getMembershipPrice', Date.now() - getPriceStart);
847
+ const wrapStart = Date.now();
848
+ const result = await wrapTransaction(async () => {
849
+ // Set gas limit instead of using estimateGas
850
+ // As the estimateGas is not reliable for this contract
851
+ return await space.Membership.write(signer).joinSpace(recipient, {
852
+ gasLimit: 1_500_000,
853
+ value: price,
854
+ });
855
+ }, txnOpts);
856
+ const blockNumberAfterTx = await space.provider?.getBlockNumber();
857
+ logger.log('joinSpace wrap', Date.now() - wrapStart, blockNumberAfterTx);
858
+ const issued = await issuedListener;
859
+ const blockNumberAfter = await space.provider?.getBlockNumber();
860
+ logger.log('joinSpace after blockNumber', Date.now() - joinSpaceStart, blockNumberAfter, result, issued);
861
+ return issued;
862
+ }
863
+ async hasSpaceMembership(spaceId, address) {
864
+ const space = this.getSpace(spaceId);
865
+ if (!space) {
866
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
867
+ }
868
+ return space.Membership.hasMembership(address);
869
+ }
870
+ async getMembershipSupply(spaceId) {
871
+ const space = this.getSpace(spaceId);
872
+ if (!space) {
873
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
874
+ }
875
+ const totalSupply = await space.ERC721A.read.totalSupply();
876
+ return { totalSupply: totalSupply.toNumber() };
877
+ }
878
+ async getMembershipInfo(spaceId) {
879
+ const space = this.getSpace(spaceId);
880
+ if (!space) {
881
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
882
+ }
883
+ const [joinSpacePriceDetails, limit, currency, feeRecipient, duration, totalSupply, pricingModule,] = await Promise.all([
884
+ this.getJoinSpacePriceDetails(spaceId),
885
+ space.Membership.read.getMembershipLimit(),
886
+ space.Membership.read.getMembershipCurrency(),
887
+ space.Ownable.read.owner(),
888
+ space.Membership.read.getMembershipDuration(),
889
+ space.ERC721A.read.totalSupply(),
890
+ space.Membership.read.getMembershipPricingModule(),
891
+ ]);
892
+ const { price, prepaidSupply, remainingFreeSupply } = joinSpacePriceDetails;
893
+ return {
894
+ price, // keep as BigNumber (wei)
895
+ maxSupply: limit.toNumber(),
896
+ currency: currency,
897
+ feeRecipient: feeRecipient,
898
+ duration: duration.toNumber(),
899
+ totalSupply: totalSupply.toNumber(),
900
+ pricingModule: pricingModule,
901
+ prepaidSupply: prepaidSupply.toNumber(),
902
+ remainingFreeSupply: remainingFreeSupply.toNumber(),
903
+ };
904
+ }
905
+ getWalletLink() {
906
+ return this.walletLink;
907
+ }
908
+ getSpace(spaceId) {
909
+ return this.spaceRegistrar.getSpace(spaceId);
910
+ }
911
+ listPricingModules() {
912
+ return this.pricingModules.listPricingModules();
913
+ }
914
+ async encodeUpdateChannelRoles(space, channelNetworkId, _updatedRoleIds) {
915
+ const channelId = ensureHexPrefix(channelNetworkId);
916
+ const encodedCallData = [];
917
+ const [channelInfo] = await Promise.all([
918
+ space.Channels.read.getChannel(channelId),
919
+ space.getEntitlementShims(),
920
+ ]);
921
+ const currentRoleIds = new Set(channelInfo.roleIds.map((r) => r.toNumber()));
922
+ const updatedRoleIds = new Set(_updatedRoleIds);
923
+ const rolesToRemove = [];
924
+ const rolesToAdd = [];
925
+ for (const r of updatedRoleIds) {
926
+ // if the current role IDs does not have the updated role ID, then that role should be added.
927
+ if (!currentRoleIds.has(r)) {
928
+ rolesToAdd.push(r);
929
+ }
930
+ }
931
+ for (const r of currentRoleIds) {
932
+ // if the updated role IDs no longer have the current role ID, then that role should be removed.
933
+ if (!updatedRoleIds.has(r)) {
934
+ rolesToRemove.push(r);
935
+ }
936
+ }
937
+ // encode the call data for each role to remove
938
+ const encodedRemoveRoles = this.encodeRemoveRolesFromChannel(space, channelId, rolesToRemove);
939
+ for (const callData of encodedRemoveRoles) {
940
+ encodedCallData.push(callData);
941
+ }
942
+ // encode the call data for each role to add
943
+ const encodedAddRoles = this.encodeAddRolesToChannel(space, channelId, rolesToAdd);
944
+ for (const callData of encodedAddRoles) {
945
+ encodedCallData.push(callData);
946
+ }
947
+ return encodedCallData;
948
+ }
949
+ encodeAddRolesToChannel(space, channelNetworkId, roleIds) {
950
+ const channelId = ensureHexPrefix(channelNetworkId);
951
+ const encodedCallData = [];
952
+ for (const roleId of roleIds) {
953
+ const encodedBytes = space.Channels.interface.encodeFunctionData('addRoleToChannel', [
954
+ channelId,
955
+ roleId,
956
+ ]);
957
+ encodedCallData.push(encodedBytes);
958
+ }
959
+ return encodedCallData;
960
+ }
961
+ encodeRemoveRolesFromChannel(space, channelNetworkId, roleIds) {
962
+ const channelId = ensureHexPrefix(channelNetworkId);
963
+ const encodedCallData = [];
964
+ for (const roleId of roleIds) {
965
+ const encodedBytes = space.Channels.interface.encodeFunctionData('removeRoleFromChannel', [channelId, roleId]);
966
+ encodedCallData.push(encodedBytes);
967
+ }
968
+ return encodedCallData;
969
+ }
970
+ async createLegacyUpdatedEntitlements(space, params) {
971
+ return createLegacyEntitlementStruct(space, params.users, params.ruleData);
972
+ }
973
+ async createUpdatedEntitlements(space, params) {
974
+ return createEntitlementStruct(space, params.users, params.ruleData);
975
+ }
976
+ async refreshMetadata(spaceId, signer, txnOpts) {
977
+ const space = this.getSpace(spaceId);
978
+ if (!space) {
979
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
980
+ }
981
+ return wrapTransaction(() => space.Membership.metadata.write(signer).refreshMetadata(), txnOpts);
982
+ }
983
+ /**
984
+ * Get the space address from the receipt and sender address
985
+ * @param receipt - The receipt from the transaction
986
+ * @param senderAddress - The address of the sender. Required for the case of a receipt containing multiple events of the same type.
987
+ * @returns The space address or undefined if the receipt is not successful
988
+ */
989
+ getSpaceAddress(receipt, senderAddress) {
990
+ if (receipt.status !== 1) {
991
+ return undefined;
992
+ }
993
+ for (const receiptLog of receipt.logs) {
994
+ const spaceAddress = this.spaceRegistrar.SpaceArchitect.getSpaceAddressFromLog(receiptLog, senderAddress);
995
+ if (spaceAddress) {
996
+ return spaceAddress;
997
+ }
998
+ }
999
+ return undefined;
1000
+ }
1001
+ getTipEvent(spaceId, receipt, senderAddress) {
1002
+ const space = this.getSpace(spaceId);
1003
+ if (!space) {
1004
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
1005
+ }
1006
+ return space.Tipping.getTipEvent(receipt, senderAddress);
1007
+ }
1008
+ withdrawSpaceFunds(spaceId, recipient, signer) {
1009
+ const space = this.getSpace(spaceId);
1010
+ if (!space) {
1011
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
1012
+ }
1013
+ return space.Treasury.write(signer).withdraw(recipient);
1014
+ }
1015
+ // If the caller doesn't provide an abort controller, listenForMembershipToken will create one
1016
+ listenForMembershipEvent(spaceId, receiver, abortController) {
1017
+ const space = this.getSpace(spaceId);
1018
+ if (!space) {
1019
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
1020
+ }
1021
+ return space.Membership.listenForMembershipToken(receiver, abortController);
1022
+ }
1023
+ /**
1024
+ * Get the token id for the owner
1025
+ * Returns the first token id matched from the linked wallets of the owner
1026
+ * @param spaceId - The space id
1027
+ * @param owner - The owner
1028
+ * @returns The token id
1029
+ */
1030
+ async getTokenIdOfOwner(spaceId, owner, config = EmptyXchainConfig) {
1031
+ const space = this.getSpace(spaceId);
1032
+ if (!space) {
1033
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
1034
+ }
1035
+ const linkedWallets = await this.getLinkedWalletsWithDelegations(owner, config);
1036
+ const tokenIds = await space.getTokenIdsOfOwner(linkedWallets);
1037
+ return tokenIds[0];
1038
+ }
1039
+ /**
1040
+ * Tip a user
1041
+ * @param args
1042
+ * @param args.spaceId - The space id
1043
+ * @param args.tokenId - The token id to tip. Obtainable from getTokenIdOfOwner
1044
+ * @param args.currency - The currency to tip - address or 0xEeeeeeeeee... for native currency
1045
+ * @param args.amount - The amount to tip
1046
+ * @param args.messageId - The message id - needs to be hex encoded to 64 characters
1047
+ * @param args.channelId - The channel id - needs to be hex encoded to 64 characters
1048
+ * @param signer - The signer to use for the tip
1049
+ * @returns The transaction
1050
+ */
1051
+ async tip(args, signer) {
1052
+ const { spaceId, tokenId, currency, amount, messageId, channelId, receiver } = args;
1053
+ const space = this.getSpace(spaceId);
1054
+ if (!space) {
1055
+ throw new Error(`Space with spaceId "${spaceId}" is not found.`);
1056
+ }
1057
+ return space.Tipping.write(signer).tip({
1058
+ receiver,
1059
+ tokenId,
1060
+ currency,
1061
+ amount,
1062
+ messageId: ensureHexPrefix(messageId),
1063
+ channelId: ensureHexPrefix(channelId),
1064
+ }, {
1065
+ value: amount,
1066
+ });
1067
+ }
1068
+ }
1069
+ // Retry submitting the transaction N times (3 by default in jest, 0 by default elsewhere)
1070
+ // and then wait until the first confirmation of the transaction has been mined
1071
+ // works around gas estimation issues and other transient issues that are more common in running CI tests
1072
+ // so by default we only retry when running under jest
1073
+ // this wrapper unifies all of the wrapped contract calls in behvior, they don't return until
1074
+ // the transaction is confirmed
1075
+ async function wrapTransaction(txFn, txnOpts) {
1076
+ const retryLimit = txnOpts?.retryCount ?? isTestEnv() ? 3 : 0;
1077
+ const runTx = async () => {
1078
+ let retryCount = 0;
1079
+ // eslint-disable-next-line no-constant-condition
1080
+ while (true) {
1081
+ try {
1082
+ const txStart = Date.now();
1083
+ const tx = await txFn();
1084
+ logger.log('Transaction submitted in', Date.now() - txStart);
1085
+ const startConfirm = Date.now();
1086
+ await confirmTransaction(tx);
1087
+ logger.log('Transaction confirmed in', Date.now() - startConfirm);
1088
+ // return the transaction, as it was successful
1089
+ // the caller can wait() on it again if they want to wait for more confirmations
1090
+ return tx;
1091
+ }
1092
+ catch (error) {
1093
+ retryCount++;
1094
+ if (retryCount >= retryLimit) {
1095
+ throw new Error('Transaction failed after retries: ' + error.message);
1096
+ }
1097
+ logger.error('Transaction submission failed, retrying...', { error, retryCount });
1098
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1099
+ }
1100
+ }
1101
+ };
1102
+ // Wait until the first confirmation of the transaction
1103
+ const confirmTransaction = async (tx) => {
1104
+ let waitRetryCount = 0;
1105
+ let errorCount = 0;
1106
+ const start = Date.now();
1107
+ // eslint-disable-next-line no-constant-condition
1108
+ while (true) {
1109
+ let receipt = null;
1110
+ try {
1111
+ receipt = await tx.wait(0);
1112
+ }
1113
+ catch (error) {
1114
+ if (typeof error === 'object' &&
1115
+ error !== null &&
1116
+ 'code' in error &&
1117
+ error.code === 'CALL_EXCEPTION') {
1118
+ logger.error('Transaction failed', { tx, errorCount, error });
1119
+ // TODO: is this a bug?
1120
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
1121
+ throw error;
1122
+ }
1123
+ // If the transaction receipt is not available yet, the error may be thrown
1124
+ // We can ignore it and retry after a short delay
1125
+ errorCount++;
1126
+ receipt = null;
1127
+ }
1128
+ if (!receipt) {
1129
+ // Transaction not minded yet, try again in 100ms
1130
+ waitRetryCount++;
1131
+ await new Promise((resolve) => setTimeout(resolve, 100));
1132
+ }
1133
+ else if (receipt.status === 1) {
1134
+ return;
1135
+ }
1136
+ else {
1137
+ logger.error('Transaction failed in an unexpected way', {
1138
+ tx,
1139
+ receipt,
1140
+ errorCount,
1141
+ });
1142
+ // Transaction failed, throw an error and the outer loop will retry
1143
+ throw new Error('Transaction confirmed but failed');
1144
+ }
1145
+ const waitRetryTime = Date.now() - start;
1146
+ // If we've been waiting for more than 20 seconds, log an error
1147
+ // and outer loop will resubmit the transaction
1148
+ if (waitRetryTime > 20_000) {
1149
+ logger.error('Transaction confirmation timed out', {
1150
+ waitRetryTime,
1151
+ waitRetryCount,
1152
+ tx,
1153
+ errorCount,
1154
+ });
1155
+ throw new Error('Transaction confirmation timed out after: ' +
1156
+ waitRetryTime +
1157
+ ' waitRetryCount: ' +
1158
+ waitRetryCount);
1159
+ }
1160
+ }
1161
+ };
1162
+ return await runTx();
1163
+ }
1164
+ //# sourceMappingURL=SpaceDapp.js.map