diff --git a/crates/js_api/src/gui/mod.rs b/crates/js_api/src/gui/mod.rs index 49bad076b..2f87c83e8 100644 --- a/crates/js_api/src/gui/mod.rs +++ b/crates/js_api/src/gui/mod.rs @@ -182,6 +182,8 @@ pub enum GuiError { TokenMustBeSelected(String), #[error("Binding has no presets: {0}")] BindingHasNoPresets(String), + #[error("Token not in select tokens: {0}")] + TokenNotInSelectTokens(String), #[error(transparent)] DotrainOrderError(#[from] DotrainOrderError), #[error(transparent)] diff --git a/crates/js_api/src/gui/state_management.rs b/crates/js_api/src/gui/state_management.rs index a256a0d0d..18e7b5b26 100644 --- a/crates/js_api/src/gui/state_management.rs +++ b/crates/js_api/src/gui/state_management.rs @@ -1,15 +1,20 @@ use super::*; +use rain_orderbook_app_settings::token::Token; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] struct SerializedGuiState { field_values: BTreeMap, deposits: BTreeMap, + select_tokens: BTreeMap, + vault_ids: BTreeMap<(bool, u8), Option>, } #[wasm_bindgen] impl DotrainOrderGui { #[wasm_bindgen(js_name = "serializeState")] - pub fn serialize(&self) -> Result { + pub fn serialize_state(&self) -> Result { + let deployment = self.get_current_deployment()?; + let mut field_values = BTreeMap::new(); for (k, v) in self.field_values.iter() { let preset = if v.is_preset { @@ -50,9 +55,27 @@ impl DotrainOrderGui { deposits.insert(k.clone(), preset); } + let mut select_tokens: BTreeMap = BTreeMap::new(); + if let Some(st) = deployment.select_tokens { + for key in st { + let token = self.dotrain_order.orderbook_yaml().get_token(&key)?; + select_tokens.insert(key, token); + } + } + + let mut vault_ids = BTreeMap::new(); + for (i, input) in deployment.deployment.order.inputs.iter().enumerate() { + vault_ids.insert((true, i as u8), input.vault_id.map(|v| v.to_string())); + } + for (i, output) in deployment.deployment.order.outputs.iter().enumerate() { + vault_ids.insert((false, i as u8), output.vault_id.map(|v| v.to_string())); + } + let state = SerializedGuiState { field_values: field_values.clone(), deposits: deposits.clone(), + select_tokens: select_tokens.clone(), + vault_ids: vault_ids.clone(), }; let bytes = bincode::serialize(&state)?; @@ -65,6 +88,7 @@ impl DotrainOrderGui { #[wasm_bindgen(js_name = "deserializeState")] pub fn deserialize_state(&mut self, serialized: String) -> Result<(), GuiError> { + let deployment = self.get_current_deployment()?; let compressed = URL_SAFE.decode(serialized)?; let mut decoder = GzDecoder::new(&compressed[..]); @@ -91,6 +115,7 @@ impl DotrainOrderGui { (k, pair_value) }) .collect::>(); + let deposits = state .deposits .into_iter() @@ -113,6 +138,40 @@ impl DotrainOrderGui { self.field_values = field_values; self.deposits = deposits; + for (key, token) in state.select_tokens { + let select_tokens = deployment + .select_tokens + .as_ref() + .ok_or(GuiError::SelectTokensNotSet)?; + if !select_tokens.contains(&key) { + return Err(GuiError::TokenNotInSelectTokens(key)); + } + if self.is_select_token_set(key.clone())? { + Token::remove_record_from_yaml( + self.dotrain_order.orderbook_yaml().documents.clone(), + &key, + )?; + } + Token::add_record_to_yaml( + self.dotrain_order.orderbook_yaml().documents.clone(), + &key, + &token.network.key, + &token.address.to_string(), + token.decimals.map(|d| d.to_string()).as_deref(), + token.label.map(|l| l.to_string()).as_deref(), + token.symbol.map(|s| s.to_string()).as_deref(), + )?; + } + + for ((is_input, index), vault_id) in state.vault_ids { + self.dotrain_order + .dotrain_yaml() + .get_order(&deployment.deployment.order.key) + .and_then(|mut order| { + order.update_vault_id(is_input, index, vault_id.unwrap_or_default()) + })?; + } + Ok(()) } diff --git a/packages/orderbook/test/js_api/gui.test.ts b/packages/orderbook/test/js_api/gui.test.ts index 1d3dcbc59..27c54aaa8 100644 --- a/packages/orderbook/test/js_api/gui.test.ts +++ b/packages/orderbook/test/js_api/gui.test.ts @@ -654,23 +654,47 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Gui', async function () describe('state management tests', async () => { let serializedState = - 'H4sIAAAAAAAA_3WNTQ5AMBCF_UW4hbWEzJQWt3AF1ZJGUgldOL6FqYXE23zz8_JeFDzKidJYZexaYUgHCDOaxkOf2hVY-s-FrGm56PoBJjkrvfzt33AWeMVEBPCFKdHtm7b4OhMih1rc8yX-NrUAAAA='; + 'H4sIAAAAAAAA_7VOyw6CMBBk1WBiPHo18QeQgqEh3E38AH4AsQqhtgSKHvx5Q9xVmhBvzGV3ZvYx4HywxmpEa7xzqS6lugFqzFkN3UcmOzFDxSVHV0IFDmGBNWJ7bo2E35E51oCx8WM2o4CtvgtPCfPUTbVFrTCmTnxf6jyThW5NErM48ps697pGvughUAf0-pieNtheRwAuLNFO-wy7AChpaqcLbTZx1sMI_malbnCuX_kxzvkbrBXoaQkCAAA='; let gui: DotrainOrderGui; beforeAll(async () => { mockServer .forPost('/rpc-url') + .once() .withBodyIncluding('0x82ad56cb') .thenSendJsonRpcResult( '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007546f6b656e203100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025431000000000000000000000000000000000000000000000000000000000000' ); - gui = await DotrainOrderGui.chooseDeployment(dotrainWithGui, 'some-deployment'); + mockServer + .forPost('/rpc-url') + .once() + .withBodyIncluding('0x82ad56cb') + .thenSendJsonRpcResult( + '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007546f6b656e203100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025431000000000000000000000000000000000000000000000000000000000000' + ); + mockServer + .forPost('/rpc-url') + .once() + .withBodyIncluding('0x82ad56cb') + .thenSendJsonRpcResult( + '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000754656b656e203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025432000000000000000000000000000000000000000000000000000000000000' + ); + let dotrain3 = `${guiConfig3} - gui.saveFieldValue('binding-1', { +${dotrain}`; + gui = await DotrainOrderGui.chooseDeployment(dotrain3, 'other-deployment'); + + gui.saveFieldValue('test-binding', { isPreset: true, - value: gui.getFieldDefinition('binding-1').presets[0].id + value: gui.getFieldDefinition('test-binding').presets[0].id }); - gui.saveFieldValue('binding-2', { isPreset: false, value: '100' }); gui.saveDeposit('token1', '50.6'); + gui.saveDeposit('token2', '100'); + gui.removeSelectToken('token1'); + gui.removeSelectToken('token2'); + await gui.saveSelectToken('token1', '0x6666666666666666666666666666666666666666'); + await gui.saveSelectToken('token2', '0x3333333333333333333333333333333333333333'); + gui.setVaultId(true, 0, '666'); + gui.setVaultId(false, 0, '333'); }); it('should serialize gui state', async () => { @@ -679,31 +703,55 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Gui', async function () }); it('should deserialize gui state', async () => { - gui.clearState(); + mockServer + .forPost('/rpc-url') + .once() + .withBodyIncluding('0x82ad56cb') + .thenSendJsonRpcResult( + '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007546f6b656e203100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025431000000000000000000000000000000000000000000000000000000000000' + ); + let dotrain3 = `${guiConfig3} + +${dotrain}`; + gui = await DotrainOrderGui.chooseDeployment(dotrain3, 'other-deployment'); + gui.removeSelectToken('token1'); + gui.removeSelectToken('token2'); + + assert.equal(gui.getAllFieldValues().length, 0); + assert.equal(gui.getDeposits().length, 0); + assert.equal(gui.isSelectTokenSet('token1'), false); + assert.equal(gui.isSelectTokenSet('token2'), false); + let oldGuiDeployment: GuiDeployment = gui.getCurrentDeployment(); + assert.equal(oldGuiDeployment.deployment.order.inputs[0].vaultId, '0x1'); + assert.equal(oldGuiDeployment.deployment.order.outputs[0].vaultId, '0x1'); + gui.deserializeState(serializedState); + const fieldValues: AllFieldValuesResult[] = gui.getAllFieldValues(); - assert.equal(fieldValues.length, 2); + assert.equal(fieldValues.length, 1); assert.deepEqual(fieldValues[0], { - binding: 'binding-1', + binding: 'test-binding', value: { id: '0', - name: 'Preset 1', - value: '0x1234567890abcdef1234567890abcdef12345678' - } - }); - assert.deepEqual(fieldValues[1], { - binding: 'binding-2', - value: { - id: '', name: undefined, - value: '100' + value: 'test-value' } }); + + assert.equal(gui.isSelectTokenSet('token1'), true); + assert.equal(gui.isSelectTokenSet('token2'), true); const deposits: TokenDeposit[] = gui.getDeposits(); - assert.equal(deposits.length, 1); + assert.equal(deposits.length, 2); assert.equal(deposits[0].token, 'token1'); assert.equal(deposits[0].amount, '50.6'); - assert.equal(deposits[0].address, '0xc2132d05d31c914a87c6611c10748aeb04b58e8f'); + assert.equal(deposits[0].address, '0x6666666666666666666666666666666666666666'); + assert.equal(deposits[1].token, 'token2'); + assert.equal(deposits[1].amount, '100'); + assert.equal(deposits[1].address, '0x3333333333333333333333333333333333333333'); + + let guiDeployment: GuiDeployment = gui.getCurrentDeployment(); + assert.equal(guiDeployment.deployment.order.inputs[0].vaultId, '0x29a'); + assert.equal(guiDeployment.deployment.order.outputs[0].vaultId, '0x14d'); }); it('should clear state', async () => { @@ -715,22 +763,22 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Gui', async function () }); it('should check if field is preset', async () => { - gui.saveFieldValue('binding-1', { + gui.saveFieldValue('test-binding', { isPreset: true, - value: gui.getFieldDefinition('binding-1').presets[0].id + value: gui.getFieldDefinition('test-binding').presets[0].id }); - assert.equal(gui.isFieldPreset('binding-1'), true); - gui.saveFieldValue('binding-2', { + assert.equal(gui.isFieldPreset('test-binding'), true); + gui.saveFieldValue('test-binding', { isPreset: false, value: '100' }); - assert.equal(gui.isFieldPreset('binding-2'), false); + assert.equal(gui.isFieldPreset('test-binding'), false); }); it('should check if deposit is preset', async () => { gui.saveDeposit('token1', '55'); assert.equal(gui.isDepositPreset('token1'), false); - gui.saveDeposit('token1', '100'); + gui.saveDeposit('token1', '0'); assert.equal(gui.isDepositPreset('token1'), true); }); });